From c136aefe20305485117c0adc3ba17c516bc05331 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 10 Mar 2025 20:29:07 +0000
Subject: [PATCH 001/193] chore: optimise unit tests (#2293)

* optimise unit tests - Cache loading the config TOML

Unit test time: 13.1s -> 10.5s

* optimise unit tests - use the same pyproject validator across the tests

Unit test time: 10.5s -> 5.7s

* optimise unit tests - selector optimisation

Unit test time: 5.7s -> 5.1s

* optimise unit tests - build options computation cache

Unit test time: 5.1s -> 3.2s

* Use functools.cache rather than functools.lru_cache

* Don't overwrite build_options when applying functools.cache
---
 cibuildwheel/options.py           | 10 ++++++--
 cibuildwheel/selector.py          | 15 +++++------
 cibuildwheel/util/resources.py    |  3 +++
 unit_test/validate_schema_test.py | 41 ++++++++++++++++---------------
 4 files changed, 38 insertions(+), 31 deletions(-)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 232e5c968..3febe36ef 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -591,6 +591,10 @@ def __init__(
         except FileNotFoundError:
             self.pyproject_toml = None
 
+        # cache the build options method so repeated calls don't need to
+        # resolve the options again
+        self.build_options = functools.cache(self._compute_build_options)
+
     @functools.cached_property
     def config_file_path(self) -> Path | None:
         args = self.command_line_arguments
@@ -667,9 +671,11 @@ def globals(self) -> GlobalOptions:
             allow_empty=allow_empty,
         )
 
-    def build_options(self, identifier: str | None) -> BuildOptions:
+    def _compute_build_options(self, identifier: str | None) -> BuildOptions:
         """
-        Compute BuildOptions for a single run configuration.
+        Compute BuildOptions for a single run configuration. Normally accessed
+        through the `build_options` method, which is the same but the result
+        is cached.
         """
 
         with self.reader.identifier(identifier):
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 1f417b7e8..bd0ce597c 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -1,7 +1,7 @@
-import fnmatch
 import itertools
 from dataclasses import dataclass
 from enum import Enum
+from fnmatch import fnmatch
 from typing import Any
 
 import bracex
@@ -18,9 +18,10 @@ def selector_matches(patterns: str, string: str) -> bool:
     expansion. For example, 'cp{36,37}-*' would match either of 'cp36-*' or
     'cp37-*'.
     """
+
     patterns_list = patterns.split()
     expanded_patterns = itertools.chain.from_iterable(bracex.expand(p) for p in patterns_list)
-    return any(fnmatch.fnmatch(string, pat) for pat in expanded_patterns)
+    return any(fnmatch(string, pat) for pat in expanded_patterns)
 
 
 class EnableGroup(Enum):
@@ -59,15 +60,11 @@ def __call__(self, build_id: str) -> bool:
                 return False
 
         # filter out groups that are not enabled
-        if EnableGroup.CPythonFreeThreading not in self.enable and selector_matches(
-            "cp3??t-*", build_id
-        ):
+        if EnableGroup.CPythonFreeThreading not in self.enable and fnmatch(build_id, "cp3??t-*"):
             return False
-        if EnableGroup.CPythonPrerelease not in self.enable and selector_matches(
-            "cp314*", build_id
-        ):
+        if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp314*"):
             return False
-        if EnableGroup.PyPy not in self.enable and selector_matches("pp*", build_id):
+        if EnableGroup.PyPy not in self.enable and fnmatch(build_id, "pp*"):
             return False
 
         should_build = selector_matches(self.build_config, build_id)
diff --git a/cibuildwheel/util/resources.py b/cibuildwheel/util/resources.py
index 7e78bc884..e27f1f892 100644
--- a/cibuildwheel/util/resources.py
+++ b/cibuildwheel/util/resources.py
@@ -1,3 +1,4 @@
+import functools
 import tomllib
 from pathlib import Path
 from typing import Final
@@ -16,6 +17,8 @@
 CIBUILDWHEEL_SCHEMA: Final[Path] = PATH / "cibuildwheel.schema.json"
 
 
+# this value is cached because it's used a lot in unit tests
+@functools.cache
 def read_python_configs(config: PlatformName) -> list[dict[str, str]]:
     with BUILD_PLATFORMS.open("rb") as f:
         loaded_file = tomllib.load(f)
diff --git a/unit_test/validate_schema_test.py b/unit_test/validate_schema_test.py
index 1670d18cb..85f5eae6b 100644
--- a/unit_test/validate_schema_test.py
+++ b/unit_test/validate_schema_test.py
@@ -10,15 +10,22 @@
 DIR = Path(__file__).parent.resolve()
 
 
-def test_validate_default_schema():
+@pytest.fixture(scope="session")
+def validator() -> validate_pyproject.api.Validator:
+    """
+    Reuse the validator for all tests, to keep unit tests fast.
+    """
+    return validate_pyproject.api.Validator()
+
+
+def test_validate_default_schema(validator: validate_pyproject.api.Validator) -> None:
     with resources.DEFAULTS.open("rb") as f:
         example = tomllib.load(f)
 
-    validator = validate_pyproject.api.Validator()
     assert validator(example) is not None
 
 
-def test_validate_container_engine():
+def test_validate_container_engine(validator: validate_pyproject.api.Validator) -> None:
     """
     This test checks container engine can be overridden - it used to be a
     global option but is now a build option.
@@ -38,12 +45,13 @@ def test_validate_container_engine():
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     assert validator(example) is not None
 
 
 @pytest.mark.parametrize("platform", ["macos", "windows"])
-def test_validate_bad_container_engine(platform: str) -> None:
+def test_validate_bad_container_engine(
+    validator: validate_pyproject.api.Validator, platform: str
+) -> None:
     """
     container-engine is not a valid option for macos or windows
     """
@@ -54,12 +62,11 @@ def test_validate_bad_container_engine(platform: str) -> None:
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     with pytest.raises(validate_pyproject.error_reporting.ValidationError):
         validator(example)
 
 
-def test_overrides_select():
+def test_overrides_select(validator: validate_pyproject.api.Validator) -> None:
     example = tomllib.loads(
         """
         [[tool.cibuildwheel.overrides]]
@@ -68,11 +75,10 @@ def test_overrides_select():
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     assert validator(example) is not None
 
 
-def test_overrides_no_select():
+def test_overrides_no_select(validator: validate_pyproject.api.Validator) -> None:
     example = tomllib.loads(
         """
         [[tool.cibuildwheel.overrides]]
@@ -80,12 +86,11 @@ def test_overrides_no_select():
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     with pytest.raises(validate_pyproject.error_reporting.ValidationError):
         validator(example)
 
 
-def test_overrides_only_select():
+def test_overrides_only_select(validator: validate_pyproject.api.Validator) -> None:
     example = tomllib.loads(
         """
         [[tool.cibuildwheel.overrides]]
@@ -93,12 +98,11 @@ def test_overrides_only_select():
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     with pytest.raises(validate_pyproject.error_reporting.ValidationError):
         validator(example)
 
 
-def test_overrides_valid_inherit():
+def test_overrides_valid_inherit(validator: validate_pyproject.api.Validator) -> None:
     example = tomllib.loads(
         """
         [[tool.cibuildwheel.overrides]]
@@ -108,11 +112,10 @@ def test_overrides_valid_inherit():
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     assert validator(example) is not None
 
 
-def test_overrides_invalid_inherit():
+def test_overrides_invalid_inherit(validator: validate_pyproject.api.Validator) -> None:
     example = tomllib.loads(
         """
         [[tool.cibuildwheel.overrides]]
@@ -122,12 +125,11 @@ def test_overrides_invalid_inherit():
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     with pytest.raises(validate_pyproject.error_reporting.ValidationError):
         validator(example)
 
 
-def test_overrides_invalid_inherit_value():
+def test_overrides_invalid_inherit_value(validator: validate_pyproject.api.Validator) -> None:
     example = tomllib.loads(
         """
         [[tool.cibuildwheel.overrides]]
@@ -137,12 +139,11 @@ def test_overrides_invalid_inherit_value():
         """
     )
 
-    validator = validate_pyproject.api.Validator()
     with pytest.raises(validate_pyproject.error_reporting.ValidationError):
         validator(example)
 
 
-def test_docs_examples():
+def test_docs_examples(validator: validate_pyproject.api.Validator) -> None:
     """
     Parse out all the configuration examples, build valid TOML out of them, and
     make sure they pass.
@@ -182,5 +183,5 @@ def test_docs_examples():
         print(example_txt)
         print()
         example = tomllib.loads(example_txt)
-        validator = validate_pyproject.api.Validator()
+
         assert validator(example) is not None

From 8c51c26bf79b8b969e237ba23323884c3d1d426a Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 10 Mar 2025 16:30:18 -0400
Subject: [PATCH 002/193] [Bot] Update dependencies (#2309)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |  2 +-
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python38.txt        |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 cibuildwheel/resources/virtualenv.toml        |  2 +-
 docs/working-examples.md                      |  8 +--
 11 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 8ce1bf4fc..1ee368fca 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -102,7 +102,7 @@ unearth==0.17.2
     # via pyodide-build
 urllib3==2.3.0
     # via requests
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via
     #   build
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index a65ea62b1..c6355b3ba 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index c5892095e..72784c87f 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.20.2
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index a65ea62b1..c6355b3ba 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index bb1293ef6..2049734c5 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,64 +1,64 @@
 [x86_64]
 manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.02.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.09-1
 
 [i686]
 manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.09-1
 
 [pypy_x86_64]
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.02.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.09-1
 
 [pypy_i686]
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.02.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.09-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.02.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.09-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.02.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.09-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.02.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.09-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.02.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.02.28-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.09-1
 
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index 962c0f45d..0d89df7aa 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1 +1 @@
-default = { version = "20.29.1", url = "https://github.com/pypa/get-virtualenv/blob/20.29.1/public/virtualenv.pyz?raw=true" }
+default = { version = "20.29.3", url = "https://github.com/pypa/get-virtualenv/blob/20.29.3/public/virtualenv.pyz?raw=true" }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 6e723c283..1d25b4aa5 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -40,8 +40,8 @@ title: Working examples
 | [UltraJSON][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings |
 | [Dependency Injector][]           | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Dependency injection framework for Python, uses Windows TravisCI |
 | [pyzmq][]                         | ![github icon][] ![circleci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython on CPython and CFFI on PyPy. ARM wheels for linux are built natively on CircleCI.  |
-| [Implicit][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels |
 | [CTranslate2][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. |
+| [Implicit][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels |
 | [tinyobjloader][]                 | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Tiny but powerful single file wavefront obj loader |
 | [vispy][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Main repository for Vispy |
 | [coverage.py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The coverage tool for Python |
@@ -94,8 +94,8 @@ title: Working examples
 | [streaming-form-data][]           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython |
 | [power-grid-model][]              | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis |
 | [bx-python][]                     | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
-| [pybase64][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
 | [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
+| [pybase64][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
 | [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
 | [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
@@ -150,8 +150,8 @@ title: Working examples
 [UltraJSON]: https://github.com/ultrajson/ultrajson
 [Dependency Injector]: https://github.com/ets-labs/python-dependency-injector
 [pyzmq]: https://github.com/zeromq/pyzmq
-[Implicit]: https://github.com/benfred/implicit
 [CTranslate2]: https://github.com/OpenNMT/CTranslate2
+[Implicit]: https://github.com/benfred/implicit
 [tinyobjloader]: https://github.com/tinyobjloader/tinyobjloader
 [vispy]: https://github.com/vispy/vispy
 [coverage.py]: https://github.com/nedbat/coveragepy
@@ -204,8 +204,8 @@ title: Working examples
 [streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data
 [power-grid-model]: https://github.com/PowerGridModel/power-grid-model
 [bx-python]: https://github.com/bxlab/bx-python
-[pybase64]: https://github.com/mayeut/pybase64
 [boost-histogram]: https://github.com/scikit-hep/boost-histogram
+[pybase64]: https://github.com/mayeut/pybase64
 [Python-WebRTC]: https://github.com/MarshalX/python-webrtc
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
 [fathon]: https://github.com/stfbnc/fathon

From 849921caef5f4fd035651b54de3229b286a53323 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 10 Mar 2025 16:30:36 -0400
Subject: [PATCH 003/193] chore(deps): bump actions/attest-build-provenance
 from 2.2.2 to 2.2.3 in the actions group (#2310)

chore(deps): bump actions/attest-build-provenance in the actions group

Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 2.2.2 to 2.2.3
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/bd77c077858b8d561b7a36cbe48ef4cc642ca39d...c074443f1aee8d4aeeae555aebba3282517141b2)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/release.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c313af46a..d9d48888b 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,7 +33,7 @@ jobs:
         path: dist
 
     - name: Generate artifact attestation for sdist and wheel
-      uses: actions/attest-build-provenance@bd77c077858b8d561b7a36cbe48ef4cc642ca39d # v2.2.2
+      uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
       with:
         subject-path: "dist/cibuildwheel-*"
 

From f5e502aa888babab1a9df466c334e7c43fb4aae1 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 10 Mar 2025 16:30:50 -0400
Subject: [PATCH 004/193] [pre-commit.ci] pre-commit autoupdate (#2311)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.9 → v0.9.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.9...v0.9.10)
- [github.com/python-jsonschema/check-jsonschema: 0.31.2 → 0.31.3](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.2...0.31.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d5bbe5417..dd425c894 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.9.9
+  rev: v0.9.10
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
@@ -81,7 +81,7 @@ repos:
 
 
 - repo: https://github.com/python-jsonschema/check-jsonschema
-  rev: 0.31.2
+  rev: 0.31.3
   hooks:
     - id: check-dependabot
     - id: check-github-actions

From 25f2d3f313f10d0dd03787ca655e40cf78da4bec Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 10 Mar 2025 21:45:54 +0000
Subject: [PATCH 005/193] Inline dependency-versions syntax (#2122)

* Write docs for inline dependency-versions

* Change the `inline` keyword to `packages` for better readability

* Implement inline package constraints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add schema for TOML properties

* Change the parsing of the option to parse filenames as-is

* Add a unit test for table-parsing of the option

* Remove unneeded shlex.quote on the dependency-version test

* Tidy-ups, comments, docs fixes

* Add test for empty packages option value

* Fix empty packages scenario

And, remove some optionals to reduce the problem space

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 bin/generate_schema.py                        |  19 ++-
 cibuildwheel/linux.py                         |  20 +--
 cibuildwheel/macos.py                         |  19 ++-
 cibuildwheel/options.py                       |  22 ++--
 cibuildwheel/pyodide.py                       |  14 +--
 .../resources/cibuildwheel.schema.json        |  40 +++++-
 cibuildwheel/util/packaging.py                | 118 ++++++++++++++----
 cibuildwheel/windows.py                       |  18 ++-
 docs/options.md                               |  26 +++-
 test/test_dependency_versions.py              |  30 +++--
 unit_test/dependency_constraints_test.py      |  50 ++++++--
 unit_test/main_tests/main_options_test.py     |  38 ++++++
 unit_test/options_test.py                     |  61 +++++++++
 13 files changed, 377 insertions(+), 98 deletions(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index 21e94b9a9..ec4229381 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -104,7 +104,24 @@
   dependency-versions:
     default: pinned
     description: Specify how cibuildwheel controls the versions of the tools it uses
-    type: string
+    oneOf:
+      - enum: [pinned, latest]
+      - type: string
+        description: Path to a file containing dependency versions, or inline package specifications, starting with "packages:"
+        not:
+          enum: [pinned, latest]
+      - type: object
+        additionalProperties: false
+        properties:
+          file:
+            type: string
+      - type: object
+        additionalProperties: false
+        properties:
+          packages:
+            type: array
+            items:
+              type: string
   enable:
     description: Enable or disable certain builds.
     oneOf:
diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py
index ecc6be6fe..3a652b366 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/linux.py
@@ -166,6 +166,7 @@ def build_in_container(
     container: OCIContainer,
     container_project_path: PurePath,
     container_package_dir: PurePath,
+    local_tmp_dir: Path,
 ) -> None:
     container_output_dir = PurePosixPath("/output")
 
@@ -199,22 +200,22 @@ def build_in_container(
 
     for config in platform_configs:
         log.build_start(config.identifier)
+        local_identifier_tmp_dir = local_tmp_dir / config.identifier
         build_options = options.build_options(config.identifier)
         build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
         use_uv = build_frontend.name == "build[uv]"
         pip = ["uv", "pip"] if use_uv else ["pip"]
 
-        dependency_constraint_flags: list[PathOrStr] = []
-
         log.step("Setting up build environment...")
 
-        if build_options.dependency_constraints:
-            constraints_file = build_options.dependency_constraints.get_for_python_version(
-                config.version
-            )
+        dependency_constraint_flags: list[PathOrStr] = []
+        local_constraints_file = build_options.dependency_constraints.get_for_python_version(
+            version=config.version,
+            tmp_dir=local_identifier_tmp_dir,
+        )
+        if local_constraints_file:
             container_constraints_file = PurePosixPath("/constraints.txt")
-
-            container.copy_into(constraints_file, container_constraints_file)
+            container.copy_into(local_constraints_file, container_constraints_file)
             dependency_constraint_flags = ["-c", container_constraints_file]
 
         env = container.get_environment()
@@ -426,7 +427,7 @@ def build_in_container(
     log.step_end()
 
 
-def build(options: Options, tmp_path: Path) -> None:  # noqa: ARG001
+def build(options: Options, tmp_path: Path) -> None:
     python_configurations = get_python_configurations(
         options.globals.build_selector, options.globals.architectures
     )
@@ -480,6 +481,7 @@ def build(options: Options, tmp_path: Path) -> None:  # noqa: ARG001
                     container=container,
                     container_project_path=container_project_path,
                     container_package_dir=container_package_dir,
+                    local_tmp_dir=tmp_path,
                 )
 
         except subprocess.CalledProcessError as error:
diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py
index 176759d0e..a6829eca9 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/macos.py
@@ -421,12 +421,12 @@ def build(options: Options, tmp_path: Path) -> None:
             config_is_arm64 = config.identifier.endswith("arm64")
             config_is_universal2 = config.identifier.endswith("universal2")
 
-            dependency_constraint_flags: Sequence[PathOrStr] = []
-            if build_options.dependency_constraints:
-                dependency_constraint_flags = [
-                    "-c",
-                    build_options.dependency_constraints.get_for_python_version(config.version),
-                ]
+            constraints_path = build_options.dependency_constraints.get_for_python_version(
+                version=config.version, tmp_dir=identifier_tmp_dir
+            )
+            dependency_constraint_flags: Sequence[PathOrStr] = (
+                ["-c", constraints_path] if constraints_path else []
+            )
 
             base_python, env = setup_python(
                 identifier_tmp_dir / "build",
@@ -463,12 +463,9 @@ def build(options: Options, tmp_path: Path) -> None:
                 build_env = env.copy()
                 if not use_uv:
                     build_env["VIRTUALENV_PIP"] = pip_version
-                if build_options.dependency_constraints:
-                    constraint_path = build_options.dependency_constraints.get_for_python_version(
-                        config.version
-                    )
+                if constraints_path:
                     combine_constraints(
-                        build_env, constraint_path, identifier_tmp_dir if use_uv else None
+                        build_env, constraints_path, identifier_tmp_dir if use_uv else None
                     )
 
                 if build_frontend.name == "pip":
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 3febe36ef..de82d6445 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -97,7 +97,7 @@ class BuildOptions:
     repair_command: str
     manylinux_images: dict[str, str] | None
     musllinux_images: dict[str, str] | None
-    dependency_constraints: DependencyConstraints | None
+    dependency_constraints: DependencyConstraints
     test_command: str | None
     before_test: str | None
     test_sources: list[str]
@@ -693,7 +693,6 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                 "config-settings", option_format=ShlexTableFormat(sep=" ", pair_sep="=")
             )
 
-            dependency_versions = self.reader.get("dependency-versions")
             test_command = self.reader.get("test-command", option_format=ListFormat(sep=" && "))
             before_test = self.reader.get("before-test", option_format=ListFormat(sep=" && "))
             test_sources = shlex.split(
@@ -739,15 +738,18 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                     with contextlib.suppress(KeyError):
                         environment.add(env_var_name, self.env[env_var_name], prepend=True)
 
-            if dependency_versions == "pinned":
-                dependency_constraints: DependencyConstraints | None = (
-                    DependencyConstraints.with_defaults()
+            dependency_versions_str = self.reader.get(
+                "dependency-versions",
+                env_plat=True,
+                option_format=ShlexTableFormat(sep="; ", pair_sep=":", allow_merge=False),
+            )
+            try:
+                dependency_constraints = DependencyConstraints.from_config_string(
+                    dependency_versions_str
                 )
-            elif dependency_versions == "latest":
-                dependency_constraints = None
-            else:
-                dependency_versions_path = Path(dependency_versions)
-                dependency_constraints = DependencyConstraints(dependency_versions_path)
+            except (ValueError, OSError) as e:
+                msg = f"Failed to parse dependency versions. {e}"
+                raise errors.ConfigurationError(msg) from e
 
             if test_extras:
                 test_extras = f"[{test_extras}]"
diff --git a/cibuildwheel/pyodide.py b/cibuildwheel/pyodide.py
index 332be9356..3c56df7e7 100644
--- a/cibuildwheel/pyodide.py
+++ b/cibuildwheel/pyodide.py
@@ -265,12 +265,12 @@ def build(options: Options, tmp_path: Path) -> None:
             built_wheel_dir.mkdir()
             repaired_wheel_dir.mkdir()
 
-            dependency_constraint_flags: Sequence[PathOrStr] = []
-            if build_options.dependency_constraints:
-                constraints_path = build_options.dependency_constraints.get_for_python_version(
-                    config.version, variant="pyodide"
-                )
-                dependency_constraint_flags = ["-c", constraints_path]
+            constraints_path = build_options.dependency_constraints.get_for_python_version(
+                version=config.version, variant="pyodide", tmp_dir=identifier_tmp_dir
+            )
+            dependency_constraint_flags: Sequence[PathOrStr] = (
+                ["-c", constraints_path] if constraints_path else []
+            )
 
             env = setup_python(
                 identifier_tmp_dir / "build",
@@ -319,7 +319,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 )
 
                 build_env = env.copy()
-                if build_options.dependency_constraints:
+                if constraints_path:
                     combine_constraints(build_env, constraints_path, identifier_tmp_dir)
                 build_env["VIRTUALENV_PIP"] = pip_version
                 call(
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index ddbadf3d1..2a811d2fe 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -233,7 +233,45 @@
     "dependency-versions": {
       "default": "pinned",
       "description": "Specify how cibuildwheel controls the versions of the tools it uses",
-      "type": "string",
+      "oneOf": [
+        {
+          "enum": [
+            "pinned",
+            "latest"
+          ]
+        },
+        {
+          "type": "string",
+          "description": "Path to a file containing dependency versions, or inline package specifications, starting with \"packages:\"",
+          "not": {
+            "enum": [
+              "pinned",
+              "latest"
+            ]
+          }
+        },
+        {
+          "type": "object",
+          "additionalProperties": false,
+          "properties": {
+            "file": {
+              "type": "string"
+            }
+          }
+        },
+        {
+          "type": "object",
+          "additionalProperties": false,
+          "properties": {
+            "packages": {
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            }
+          }
+        }
+      ],
       "title": "CIBW_DEPENDENCY_VERSIONS"
     },
     "enable": {
diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py
index 3d284820a..2c8795b8d 100644
--- a/cibuildwheel/util/packaging.py
+++ b/cibuildwheel/util/packaging.py
@@ -1,4 +1,6 @@
+import shlex
 from collections.abc import Mapping, MutableMapping, Sequence
+from dataclasses import dataclass, field
 from pathlib import Path, PurePath
 from typing import Any, Literal, Self, TypeVar
 
@@ -6,47 +8,109 @@
 
 from . import resources
 from .cmd import call
+from .helpers import parse_key_value_string, unwrap
 
 
+@dataclass()
 class DependencyConstraints:
-    def __init__(self, base_file_path: Path):
-        assert base_file_path.exists()
-        self.base_file_path = base_file_path.resolve()
+    base_file_path: Path | None = None
+    packages: list[str] = field(default_factory=list)
+
+    def __post_init__(self) -> None:
+        if self.packages and self.base_file_path is not None:
+            msg = "Cannot specify both a file and packages in the dependency constraints"
+            raise ValueError(msg)
+
+        if self.base_file_path is not None:
+            if not self.base_file_path.exists():
+                msg = f"Dependency constraints file not found: {self.base_file_path}"
+                raise FileNotFoundError(msg)
+            self.base_file_path = self.base_file_path.resolve()
 
     @classmethod
-    def with_defaults(cls) -> Self:
+    def pinned(cls) -> Self:
         return cls(base_file_path=resources.CONSTRAINTS)
 
-    def get_for_python_version(
-        self, version: str, *, variant: Literal["python", "pyodide"] = "python"
-    ) -> Path:
-        version_parts = version.split(".")
-
-        # try to find a version-specific dependency file e.g. if
-        # ./constraints.txt is the base, look for ./constraints-python36.txt
-        specific_stem = self.base_file_path.stem + f"-{variant}{version_parts[0]}{version_parts[1]}"
-        specific_name = specific_stem + self.base_file_path.suffix
-        specific_file_path = self.base_file_path.with_name(specific_name)
-
-        if specific_file_path.exists():
-            return specific_file_path
-        else:
-            return self.base_file_path
+    @classmethod
+    def latest(cls) -> Self:
+        return cls()
 
-    def __repr__(self) -> str:
-        return f"{self.__class__.__name__}({self.base_file_path!r})"
+    @classmethod
+    def from_config_string(cls, config_string: str) -> Self:
+        if config_string == "pinned":
+            return cls.pinned()
+
+        if config_string == "latest" or not config_string:
+            return cls.latest()
+
+        if config_string.startswith(("file:", "packages:")):
+            # we only do the table-style parsing if it looks like a table,
+            # because this option used to be only a file path. We don't want
+            # to break existing configurations, whose file paths might include
+            # special characters like ':' or ' ', which would require quoting
+            # if they were to be passed as a parse_key_value_string positional
+            # argument.
+            return cls.from_table_style_config_string(config_string)
 
-    def __eq__(self, o: object) -> bool:
-        if not isinstance(o, DependencyConstraints):
-            return False
+        return cls(base_file_path=Path(config_string))
+
+    @classmethod
+    def from_table_style_config_string(cls, config_string: str) -> Self:
+        config_dict = parse_key_value_string(config_string, kw_arg_names=["file", "packages"])
+        files = config_dict.get("file")
+        packages = config_dict.get("packages") or []
 
-        return self.base_file_path == o.base_file_path
+        if files and packages:
+            msg = "Cannot specify both a file and packages in dependency-versions"
+            raise ValueError(msg)
+
+        if files:
+            if len(files) > 1:
+                msg = unwrap("""
+                    Only one file can be specified in dependency-versions.
+                    If you intended to pass only one, perhaps you need to quote the path?
+                """)
+                raise ValueError(msg)
+
+            return cls(base_file_path=Path(files[0]))
+
+        return cls(packages=packages)
+
+    def get_for_python_version(
+        self, *, version: str, variant: Literal["python", "pyodide"] = "python", tmp_dir: Path
+    ) -> Path | None:
+        if self.packages:
+            constraint_file = tmp_dir / "constraints.txt"
+            constraint_file.write_text("\n".join(self.packages))
+            return constraint_file
+
+        if self.base_file_path is not None:
+            version_parts = version.split(".")
+
+            # try to find a version-specific dependency file e.g. if
+            # ./constraints.txt is the base, look for ./constraints-python36.txt
+            specific_stem = (
+                self.base_file_path.stem + f"-{variant}{version_parts[0]}{version_parts[1]}"
+            )
+            specific_name = specific_stem + self.base_file_path.suffix
+            specific_file_path = self.base_file_path.with_name(specific_name)
+
+            if specific_file_path.exists():
+                return specific_file_path
+            else:
+                return self.base_file_path
+
+        return None
 
     def options_summary(self) -> Any:
-        if self == DependencyConstraints.with_defaults():
+        if self == DependencyConstraints.pinned():
             return "pinned"
-        else:
+        elif self.packages:
+            return {"packages": " ".join(shlex.quote(p) for p in self.packages)}
+        elif self.base_file_path is not None:
             return self.base_file_path.name
+        else:
+            return "latest"
 
 
 def get_pip_version(env: Mapping[str, str]) -> str:
diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py
index d07d07078..268e73de6 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/windows.py
@@ -364,12 +364,13 @@ def build(options: Options, tmp_path: Path) -> None:
             built_wheel_dir = identifier_tmp_dir / "built_wheel"
             repaired_wheel_dir = identifier_tmp_dir / "repaired_wheel"
 
-            dependency_constraint_flags: Sequence[PathOrStr] = []
-            if build_options.dependency_constraints:
-                dependency_constraint_flags = [
-                    "-c",
-                    build_options.dependency_constraints.get_for_python_version(config.version),
-                ]
+            constraints_path = build_options.dependency_constraints.get_for_python_version(
+                version=config.version,
+                tmp_dir=identifier_tmp_dir,
+            )
+            dependency_constraint_flags: Sequence[PathOrStr] = (
+                ["-c", constraints_path] if constraints_path else []
+            )
 
             # install Python
             base_python, env = setup_python(
@@ -411,10 +412,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 if not use_uv:
                     build_env["VIRTUALENV_PIP"] = pip_version
 
-                if build_options.dependency_constraints:
-                    constraints_path = build_options.dependency_constraints.get_for_python_version(
-                        config.version
-                    )
+                if constraints_path:
                     combine_constraints(build_env, constraints_path, identifier_tmp_dir)
 
                 if build_frontend.name == "pip":
diff --git a/docs/options.md b/docs/options.md
index d36091227..0dfaf0a72 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1356,9 +1356,10 @@ Options can be supplied after the name.
 
 
 ### `CIBW_DEPENDENCY_VERSIONS` {: #dependency-versions}
-> Specify how cibuildwheel controls the versions of the tools it uses
 
-Options: `pinned` `latest` `<your constraints file>`
+> Control the versions of the tools cibuildwheel uses
+
+Options: `pinned` `latest` `packages: SPECIFIER...` `<your constraints file>`
 
 Default: `pinned`
 
@@ -1373,7 +1374,8 @@ fixes that can't wait for a new cibuildwheel release.
 
 To control the versions of dependencies yourself, you can supply a [pip
 constraints](https://pip.pypa.io/en/stable/user_guide/#constraints-files) file
-here and it will be used instead.
+here and it will be used instead. Alternatively, you can list constraint
+specifiers inline with the `packages: SPECIFIER...` syntax.
 
 !!! note
     If you need different dependencies for each python version, provide them
@@ -1404,6 +1406,15 @@ Platform-specific environment variables are also available:<br/>
 
     # Use your own pip constraints file
     CIBW_DEPENDENCY_VERSIONS: ./constraints.txt
+
+    # Specify requirements inline
+    CIBW_DEPENDENCY_VERSIONS: "packages: auditwheel==6.2.0"
+
+    # Choose a specific pyodide-build version
+    CIBW_DEPENDENCY_VERSIONS_PYODIDE: "packages: pyodide-build==0.29.1"
+
+    # Use shell-style quoting around spaces package specifiers
+    CIBW_DEPENDENCY_VERSIONS: "packages: 'pip >=16.0.0, !=17'"
     ```
 
 !!! tab examples "pyproject.toml"
@@ -1417,7 +1428,14 @@ Platform-specific environment variables are also available:<br/>
     dependency-versions = "latest"
 
     # Use your own pip constraints file
-    dependency-versions = "./constraints.txt"
+    dependency-versions = { file = "./constraints.txt" }
+
+    # Specify requirements inline
+    dependency-versions = { packages = ["auditwheel==6.2.0"] }
+
+    [tool.cibuildwheel.pyodide]
+    # Choose a specific pyodide-build version
+    dependency-versions = { packages = ["pyodide-build==0.29.1"] }
     ```
 
 
diff --git a/test/test_dependency_versions.py b/test/test_dependency_versions.py
index 2cb8a583e..b264868f3 100644
--- a/test/test_dependency_versions.py
+++ b/test/test_dependency_versions.py
@@ -91,7 +91,8 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv):
     assert set(actual_wheels) == set(expected_wheels)
 
 
-def test_dependency_constraints_file(tmp_path, build_frontend_env_nouv):
+@pytest.mark.parametrize("method", ["inline", "file"])
+def test_dependency_constraints(method, tmp_path, build_frontend_env_nouv):
     if utils.platform == "linux":
         pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
 
@@ -103,15 +104,24 @@ def test_dependency_constraints_file(tmp_path, build_frontend_env_nouv):
         "delocate": "0.10.3",
     }
 
-    constraints_file = tmp_path / "constraints file.txt"
-    constraints_file.write_text(
-        textwrap.dedent(
-            """
-            pip=={pip}
-            delocate=={delocate}
-            """.format(**tool_versions)
+    if method == "file":
+        constraints_file = tmp_path / "constraints file.txt"
+        constraints_file.write_text(
+            textwrap.dedent(
+                """
+                pip=={pip}
+                delocate=={delocate}
+                """.format(**tool_versions)
+            )
         )
-    )
+        dependency_version_option = str(constraints_file)
+    elif method == "inline":
+        dependency_version_option = "packages: " + " ".join(
+            f"{k}=={v}" for k, v in tool_versions.items()
+        )
+    else:
+        msg = f"Unknown method: {method}"
+        raise ValueError(msg)
 
     build_environment = {}
 
@@ -126,7 +136,7 @@ def test_dependency_constraints_file(tmp_path, build_frontend_env_nouv):
         project_dir,
         add_env={
             "CIBW_ENVIRONMENT": cibw_environment_option,
-            "CIBW_DEPENDENCY_VERSIONS": str(constraints_file),
+            "CIBW_DEPENDENCY_VERSIONS": dependency_version_option,
             **build_frontend_env_nouv,
         },
     )
diff --git a/unit_test/dependency_constraints_test.py b/unit_test/dependency_constraints_test.py
index 5fcdd600f..78d4ab9e3 100644
--- a/unit_test/dependency_constraints_test.py
+++ b/unit_test/dependency_constraints_test.py
@@ -1,21 +1,55 @@
 from pathlib import Path
 
+import pytest
+
 from cibuildwheel.util.packaging import DependencyConstraints
 
 
-def test_defaults():
-    dependency_constraints = DependencyConstraints.with_defaults()
+def test_defaults(tmp_path: Path) -> None:
+    dependency_constraints = DependencyConstraints.pinned()
 
     project_root = Path(__file__).parents[1]
     resources_dir = project_root / "cibuildwheel" / "resources"
 
+    assert dependency_constraints.base_file_path
     assert dependency_constraints.base_file_path.samefile(resources_dir / "constraints.txt")
-    assert dependency_constraints.get_for_python_version("3.99").samefile(
-        resources_dir / "constraints.txt"
+
+    constraints_file = dependency_constraints.get_for_python_version(
+        version="3.99", tmp_dir=tmp_path
+    )
+    assert constraints_file
+    assert constraints_file.samefile(resources_dir / "constraints.txt")
+
+    constraints_file = dependency_constraints.get_for_python_version(
+        version="3.9", tmp_dir=tmp_path
     )
-    assert dependency_constraints.get_for_python_version("3.9").samefile(
-        resources_dir / "constraints-python39.txt"
+    assert constraints_file
+    assert constraints_file.samefile(resources_dir / "constraints-python39.txt")
+
+    constraints_file = dependency_constraints.get_for_python_version(
+        version="3.13", tmp_dir=tmp_path
     )
-    assert dependency_constraints.get_for_python_version("3.13").samefile(
-        resources_dir / "constraints-python313.txt"
+    assert constraints_file
+    assert constraints_file.samefile(resources_dir / "constraints-python313.txt")
+
+
+def test_inline_packages(tmp_path: Path) -> None:
+    dependency_constraints = DependencyConstraints(
+        base_file_path=None,
+        packages=["foo==1.2.3", "bar==4.5.6"],
     )
+
+    constraint_file = dependency_constraints.get_for_python_version(version="x.x", tmp_dir=tmp_path)
+    assert constraint_file
+    constraints_file_contents = constraint_file.read_text()
+
+    assert constraints_file_contents == "foo==1.2.3\nbar==4.5.6"
+
+
+@pytest.mark.parametrize("config_string", ["", "latest", "packages:"])
+def test_empty_constraints(config_string: str) -> None:
+    dependency_constraints = DependencyConstraints.from_config_string(config_string)
+
+    assert not dependency_constraints.packages
+    assert not dependency_constraints.base_file_path
+    assert dependency_constraints == DependencyConstraints.latest()
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index c020a7ced..2aaebda43 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -11,6 +11,7 @@
 from cibuildwheel.options import BuildOptions, _get_pinned_container_images
 from cibuildwheel.selector import BuildSelector, EnableGroup
 from cibuildwheel.util import resources
+from cibuildwheel.util.packaging import DependencyConstraints
 
 # CIBW_PLATFORM is tested in main_platform_test.py
 
@@ -342,6 +343,43 @@ def test_before_all(before_all, platform_specific, platform, intercepted_build_a
     assert build_options.before_all == (before_all or "")
 
 
+@pytest.mark.parametrize(
+    "dependency_versions",
+    [None, "pinned", "latest", "FILE", "packages: pip==21.0.0"],
+)
+@pytest.mark.parametrize("platform_specific", [False, True])
+def test_dependency_versions(
+    dependency_versions, platform_specific, platform, intercepted_build_args, monkeypatch, tmp_path
+):
+    option_value = dependency_versions
+
+    if dependency_versions == "FILE":
+        constraints_file = tmp_path / "constraints.txt"
+        constraints_file.write_text("foo==1.2.3\nbar==4.5.6")
+        option_value = str(constraints_file)
+
+    if option_value is not None:
+        if platform_specific:
+            monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS_" + platform.upper(), option_value)
+            monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS", "overwritten")
+        else:
+            monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS", option_value)
+
+    main()
+
+    build_options: BuildOptions = intercepted_build_args.args[0].build_options(identifier=None)
+    dependency_constraints = build_options.dependency_constraints
+    if dependency_versions is None or dependency_versions == "pinned":
+        assert dependency_constraints == DependencyConstraints.pinned()
+    elif dependency_versions == "latest":
+        assert dependency_constraints == DependencyConstraints.latest()
+    elif dependency_versions == "FILE":
+        assert dependency_constraints.base_file_path
+        assert dependency_constraints.base_file_path.samefile(Path(option_value))
+    elif dependency_versions.startswith("packages:"):
+        assert dependency_constraints.packages == ["pip==21.0.0"]
+
+
 @pytest.mark.parametrize("method", ["unset", "command_line", "env_var"])
 def test_debug_traceback(monkeypatch, method, capfd):
     if method == "command_line":
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index fe6f9c341..1bd67aa77 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -14,6 +14,8 @@
     _get_pinned_container_images,
 )
 from cibuildwheel.selector import EnableGroup
+from cibuildwheel.util import resources
+from cibuildwheel.util.packaging import DependencyConstraints
 
 PYPROJECT_1 = """
 [tool.cibuildwheel]
@@ -468,3 +470,62 @@ def test_free_threaded_support(
         assert EnableGroup.CPythonFreeThreading in options.globals.build_selector.enable
     else:
         assert EnableGroup.CPythonFreeThreading not in options.globals.build_selector.enable
+
+
+@pytest.mark.parametrize(
+    ("toml_assignment", "base_file_path", "packages"),
+    [
+        ("", resources.CONSTRAINTS, []),
+        ("dependency-versions = 'pinned'", resources.CONSTRAINTS, []),
+        ("dependency-versions = 'latest'", None, []),
+        ("dependency-versions = 'constraints file.txt'", Path("constraints file.txt"), []),
+        (
+            "dependency-versions = \"file:'constraints file.txt'\"",
+            Path("constraints file.txt"),
+            [],
+        ),
+        (
+            "dependency-versions = {file = 'constraints file.txt'}",
+            Path("constraints file.txt"),
+            [],
+        ),
+        (
+            "dependency-versions = 'packages: foo==1.2.3 bar==4.5.6'",
+            None,
+            ["foo==1.2.3", "bar==4.5.6"],
+        ),
+    ],
+)
+def test_dependency_versions_toml(
+    tmp_path: Path,
+    toml_assignment: str,
+    base_file_path: Path | None,
+    packages: list[str] | None,
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:
+    args = CommandLineArguments.defaults()
+    args.package_dir = tmp_path
+
+    (tmp_path / "constraints file.txt").write_text("")
+    monkeypatch.chdir(tmp_path)
+
+    pyproject_toml: Path = tmp_path / "pyproject.toml"
+    pyproject_toml.write_text(
+        textwrap.dedent(
+            f"""\
+            [tool.cibuildwheel]
+            {toml_assignment}
+            """
+        )
+    )
+
+    options = Options(platform="linux", command_line_arguments=args, env={})
+    parsed_dependency_constraints = options.build_options(None).dependency_constraints
+    if base_file_path is None and packages is None:
+        assert parsed_dependency_constraints == DependencyConstraints.latest()
+    else:
+        if parsed_dependency_constraints.base_file_path and base_file_path:
+            assert parsed_dependency_constraints.base_file_path.samefile(base_file_path)
+        else:
+            assert parsed_dependency_constraints.base_file_path == base_file_path
+        assert parsed_dependency_constraints.packages == packages

From 915d15cc32a1cdd3dc94641c637d19c9c2fd1c7f Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 11 Mar 2025 16:44:06 -0400
Subject: [PATCH 006/193] fix: warn on deprecated images being set (#2312)

* fix: warn on deprecated images being set

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: handle repeated warning messages

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Joe Rickerby <joerick@mac.com>

* fix: try making the warnings once per Options

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 cibuildwheel/options.py | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index de82d6445..aff0566c2 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -576,6 +576,7 @@ def __init__(
         self.command_line_arguments = command_line_arguments
         self.env = env
         self._defaults = defaults
+        self._image_warnings = set[str]()
 
         self.reader = OptionsReader(
             None if defaults else self.config_file_path,
@@ -777,6 +778,24 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                         # default to manylinux2014
                         image = pinned_images["manylinux2014"]
                     elif config_value in pinned_images:
+                        if (
+                            config_value
+                            in {
+                                "manylinux1",
+                                "manylinux2010",
+                                "manylinux_2_24",
+                                "musllinux_1_1",
+                            }
+                            and config_value not in self._image_warnings
+                        ):
+                            self._image_warnings.add(config_value)
+                            msg = (
+                                f"Deprecated image {config_value!r}. This value will not work"
+                                " in a future version of cibuildwheel. Either upgrade to a supported"
+                                " image or continue using the deprecated image by pinning directly"
+                                f" to {pinned_images[config_value]!r}."
+                            )
+                            log.warning(msg)
                         image = pinned_images[config_value]
                     else:
                         image = config_value

From eefd48e266af6b62788356a3cf9fb2a15f3f9395 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Tue, 11 Mar 2025 21:44:25 +0100
Subject: [PATCH 007/193] chore: only build on push to main for Travis CI
 (#2295)

---
 .travis.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index f3989233a..ec2365a09 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,9 @@ branches:
   only:
   - main
 
+# only build the main branch or PR that explicitely want to test with Travis CI
+if: (type = "push") OR (commit_message =~ /travis-ci/)
+
 jobs:
   include:
     - name: Linux | x86_64 + i686 | Python 3.12

From 26e12290b55e1347bb08f6da1c7be1d018c5036e Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Wed, 12 Mar 2025 04:48:35 +0800
Subject: [PATCH 008/193] feat: add support for building iOS wheels. (#2286)

* Add support for building iOS wheels.

* Replace use of system() in test binary module.

* Restored the 'minimal' approach of the minimal examples.

* Split out platform details into standalone pages, and expand iOS platform details.

* More doc corrections.

* Bump support package to include fix for python/cpython#130292

* Ensure iOS tests are all run on the same xdist worker.

* More iOS documentation tweaks.

* Factor out common xcode version test utility.

* Simplify iOS to a single platform with an expanded interpretation of arch.

* I guess I should update the iOS tests as well...

* Additional safety for missing iOS test output.

* Remove DYLD_LIBRARY_PATH from the iOS environment.

* Make test-sources mandatory for iOS builds.

* Updates and clarifications to documentation.

* Clarify what a slice is.

* Normalize use of underscores in platform name.

* Modify auto target to be matching CPU only.

* Use consistent ordering of platforms in examples.

* Use consistent naming in iOS archiectures.

* Placate the linter.

* Miscellaneous cleanups picked up by @joerick's review.

* Correct the list of expected wheels.

* Correct which 'native' we're actually checking.

* Correct the docs links so they're all relative.

* Correct the identification of free threaded builds.

* Use target instead of host to describe the platform we're building for.

* Rework iOS test to remove issue with log completeness.

* Convert errors to FatalError

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>

* Removed a repeated check for a valid python.

* Update bin/update_pythons.py to update iOS support packages.

* Document that iOS CI is available on other platforms.

* Restore a comment needed for some platforms.

* Small cleanups identified in code review

Co-authored-by: Joe Rickerby <joerick@mac.com>

* Simplify logic to appease linter.

* Modify dependency constraint handling to use new API.

* Cosmetic change to trigger a CI rebuild.

---------

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 README.md                                     |  48 +-
 bin/generate_schema.py                        |   1 +
 bin/run_tests.py                              |   3 +
 bin/update_pythons.py                         |  61 +-
 cibuildwheel/__main__.py                      |  16 +-
 cibuildwheel/architecture.py                  |  36 +-
 cibuildwheel/ios.py                           | 586 ++++++++++++++++++
 cibuildwheel/logger.py                        |   3 +
 cibuildwheel/resources/build-platforms.toml   |   7 +
 .../resources/cibuildwheel.schema.json        |  45 ++
 cibuildwheel/resources/defaults.toml          |   2 +
 cibuildwheel/typing.py                        |   2 +-
 cibuildwheel/util/packaging.py                |   7 +-
 docs/contributing.md                          |   2 +-
 docs/options.md                               |  84 +--
 docs/platforms/ios.md                         |  68 ++
 docs/platforms/linux.md                       |  13 +
 docs/platforms/macos.md                       |  46 ++
 docs/platforms/pyodide.md                     |  13 +
 docs/platforms/windows.md                     |  15 +
 docs/setup.md                                 | 131 +---
 examples/github-deploy.yml                    |  37 +-
 examples/github-minimal.yml                   |   2 +-
 examples/github-pipx.yml                      |  23 +
 mkdocs.yml                                    |  24 +-
 test/test_emulation.py                        |   4 +-
 test/test_ios.py                              |  95 +++
 test/test_macos_archs.py                      |  23 +-
 test/test_projects/c.py                       |  11 +-
 test/test_testing.py                          |   6 +-
 test/utils.py                                 |  15 +
 31 files changed, 1189 insertions(+), 240 deletions(-)
 create mode 100644 cibuildwheel/ios.py
 create mode 100644 docs/platforms/ios.md
 create mode 100644 docs/platforms/linux.md
 create mode 100644 docs/platforms/macos.md
 create mode 100644 docs/platforms/pyodide.md
 create mode 100644 docs/platforms/windows.md
 create mode 100644 examples/github-pipx.yml
 create mode 100644 test/test_ios.py

diff --git a/README.md b/README.md
index 0a391a733..028b8e53d 100644
--- a/README.md
+++ b/README.md
@@ -24,18 +24,18 @@ What does it do?
 
 While cibuildwheel itself requires a recent Python version to run (we support the last three releases), it can target the following versions to build wheels:
 
-|                | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | Pyodide |
-|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|
-| CPython 3.8    | ✅ | ✅  | ✅  | ✅  | N/A | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A |
-| CPython 3.9    | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅ | ✅ | ✅  | ✅  | ✅⁵ | N/A |
-| CPython 3.10   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A |
-| CPython 3.11   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A |
-| CPython 3.12   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅⁴ |
-| CPython 3.13³  | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A |
-| PyPy 3.8 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A |
-| PyPy 3.9 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A |
-| PyPy 3.10 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A |
-| PyPy 3.11 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A |
+|                | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | iOS | Pyodide |
+|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|-----|
+| CPython 3.8    | ✅ | ✅  | ✅  | ✅  | N/A | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.9    | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅ | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.10   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.11   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.12   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | ✅⁴ |
+| CPython 3.13³  | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
+| PyPy 3.8 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.9 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.10 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.11 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
 
 <sup>¹ PyPy is only supported for manylinux wheels.</sup><br>
 <sup>² Windows arm64 support is experimental.</sup><br>
@@ -55,18 +55,19 @@ Usage
 
 `cibuildwheel` runs inside a CI service. Supported platforms depend on which service you're using:
 
-|                 | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM |
-|-----------------|-------|-------|---------|-----------|-----------|-------------|
-| GitHub Actions  | ✅    | ✅    | ✅       | ✅        | ✅        | ✅²         |
-| Azure Pipelines | ✅    | ✅    | ✅       |           | ✅        | ✅²         |
-| Travis CI       | ✅    |       | ✅      | ✅        |           |             |
-| AppVeyor        | ✅    | ✅    | ✅      |           | ✅        | ✅²         |
-| CircleCI        | ✅    | ✅    |         | ✅        | ✅        |             |
-| Gitlab CI       | ✅    | ✅    | ✅      | ✅¹       | ✅        |             |
-| Cirrus CI       | ✅    | ✅    | ✅      | ✅        | ✅        |             |
+|                 | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS |
+|-----------------|-------|-------|---------|-----------|-----------|-------------|-----|
+| GitHub Actions  | ✅    | ✅    | ✅       | ✅        | ✅        | ✅²         | ✅³  |
+| Azure Pipelines | ✅    | ✅    | ✅       |           | ✅        | ✅²         | ✅³  |
+| Travis CI       | ✅    |       | ✅      | ✅        |           |             |     |
+| AppVeyor        | ✅    | ✅    | ✅      |           | ✅        | ✅²         | ✅³  |
+| CircleCI        | ✅    | ✅    |         | ✅        | ✅        |             | ✅³  |
+| Gitlab CI       | ✅    | ✅    | ✅      | ✅¹       | ✅        |             | ✅³  |
+| Cirrus CI       | ✅    | ✅    | ✅      | ✅        | ✅        |             | ✅³  |
 
 <sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#emulation), distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.</sup><br>
-<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup>
+<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
+<sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture.</sup>
 
 <!--intro-end-->
 
@@ -75,6 +76,7 @@ Example setup
 
 To build manylinux, musllinux, macOS, and Windows wheels on GitHub Actions, you could use this `.github/workflows/wheels.yml`:
 
+<!--generic-github-start-->
 ```yaml
 name: Build
 
@@ -102,12 +104,14 @@ jobs:
         # to supply options, put them in 'env', like:
         # env:
         #   CIBW_SOME_OPTION: value
+        #   ...
 
       - uses: actions/upload-artifact@v4
         with:
           name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
           path: ./wheelhouse/*.whl
 ```
+<!--generic-github-end-->
 
 For more information, including PyPI deployment, and the use of other CI services or the dedicated GitHub Action, check out the [documentation](https://cibuildwheel.pypa.io) and the [examples](https://github.com/pypa/cibuildwheel/tree/main/examples).
 
diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index ec4229381..b82a91136 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -322,6 +322,7 @@ def as_object(d: dict[str, Any]) -> dict[str, Any]:
     "windows": as_object(not_linux),
     "macos": as_object(not_linux),
     "pyodide": as_object(not_linux),
+    "ios": as_object(not_linux),
 }
 
 oses["linux"]["properties"]["repair-wheel-command"] = {
diff --git a/bin/run_tests.py b/bin/run_tests.py
index e4ea6311d..2aed4f01a 100755
--- a/bin/run_tests.py
+++ b/bin/run_tests.py
@@ -41,12 +41,15 @@
         sys.executable,
         "-m",
         "pytest",
+        "--dist",
+        "loadgroup",
         f"--numprocesses={args.num_processes}",
         "-x",
         "--durations",
         "0",
         "--timeout=2400",
         "test",
+        "-vv",
     ]
 
     if sys.platform.startswith("linux") and args.run_podman:
diff --git a/bin/update_pythons.py b/bin/update_pythons.py
index 035acbd9b..7b8ea1474 100755
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -44,13 +44,13 @@ class ConfigWinPP(TypedDict):
     url: str
 
 
-class ConfigMacOS(TypedDict):
+class ConfigApple(TypedDict):
     identifier: str
     version: str
     url: str
 
 
-AnyConfig = ConfigWinCP | ConfigWinPP | ConfigMacOS
+AnyConfig = ConfigWinCP | ConfigWinPP | ConfigApple
 
 
 # The following set of "Versions" classes allow the initial call to the APIs to
@@ -154,7 +154,7 @@ def update_version_windows(self, spec: Specifier) -> ConfigWinCP:
             url=url,
         )
 
-    def update_version_macos(self, spec: Specifier) -> ConfigMacOS:
+    def update_version_macos(self, spec: Specifier) -> ConfigApple:
         if self.arch not in {"64", "ARM64"}:
             msg = f"'{self.arch}' arch not supported yet on macOS"
             raise RuntimeError(msg)
@@ -178,7 +178,7 @@ def update_version_macos(self, spec: Specifier) -> ConfigMacOS:
             if "" in rf["platform"] == "darwin" and rf["arch"] == arch
         )
 
-        return ConfigMacOS(
+        return ConfigApple(
             identifier=identifier,
             version=f"{version.major}.{version.minor}",
             url=url,
@@ -204,7 +204,7 @@ def __init__(self) -> None:
 
     def update_version_macos(
         self, identifier: str, version: Version, spec: Specifier
-    ) -> ConfigMacOS | None:
+    ) -> ConfigApple | None:
         # see note above on Specifier.filter
         unsorted_versions = spec.filter(self.versions_dict)
         sorted_versions = sorted(unsorted_versions, reverse=True)
@@ -223,7 +223,7 @@ def update_version_macos(
 
             urls = [rf["url"] for rf in file_info if file_ident in rf["url"]]
             if urls:
-                return ConfigMacOS(
+                return ConfigApple(
                     identifier=identifier,
                     version=f"{new_version.major}.{new_version.minor}",
                     url=urls[0],
@@ -232,6 +232,48 @@ def update_version_macos(
         return None
 
 
+class CPythonIOSVersions:
+    def __init__(self) -> None:
+        response = requests.get(
+            "https://api.github.com/repos/beeware/Python-Apple-support/releases",
+            headers={
+                "Accept": "application/vnd.github+json",
+                "X-Github-Api-Version": "2022-11-28",
+            },
+        )
+        response.raise_for_status()
+
+        releases_info = response.json()
+        self.versions_dict: dict[Version, dict[int, str]] = {}
+
+        # Each release has a name like "3.13-b4"
+        for release in releases_info:
+            py_version, build = release["name"].split("-")
+            version = Version(py_version)
+            self.versions_dict.setdefault(version, {})
+
+            # There are several release assets associated with each release;
+            # The name of the asset will be something like
+            # "Python-3.11-iOS-support.b4.tar.gz". Store all builds that are
+            # "-iOS-support" builds, retaining the download URL.
+            for asset in release["assets"]:
+                filename, build, _, _ = asset["name"].rsplit(".", 3)
+                if filename.endswith("-iOS-support"):
+                    self.versions_dict[version][int(build[1:])] = asset["browser_download_url"]
+
+    def update_version_ios(self, identifier: str, version: Version) -> ConfigApple | None:
+        # Return a config using the highest build number for the given version.
+        urls = [url for _, url in sorted(self.versions_dict.get(version, {}).items())]
+        if urls:
+            return ConfigApple(
+                identifier=identifier,
+                version=str(version),
+                url=urls[-1],
+            )
+
+        return None
+
+
 # This is a universal interface to all the above Versions classes. Given an
 # identifier, it updates a config dict.
 
@@ -250,6 +292,8 @@ def __init__(self) -> None:
         self.macos_pypy = PyPyVersions("64")
         self.macos_pypy_arm64 = PyPyVersions("ARM64")
 
+        self.ios_cpython = CPythonIOSVersions()
+
     def update_config(self, config: MutableMapping[str, str]) -> None:
         identifier = config["identifier"]
         version = Version(config["version"])
@@ -282,6 +326,8 @@ def update_config(self, config: MutableMapping[str, str]) -> None:
             config_update = self.windows_t_arm64.update_version_windows(spec)
         elif "win_arm64" in identifier and identifier.startswith("cp"):
             config_update = self.windows_arm64.update_version_windows(spec)
+        elif "ios" in identifier:
+            config_update = self.ios_cpython.update_version_ios(identifier, version)
 
         assert config_update is not None, f"{identifier} not found!"
         config.update(**config_update)
@@ -317,6 +363,9 @@ def update_pythons(force: bool, level: str) -> None:
     for config in configs["macos"]["python_configurations"]:
         all_versions.update_config(config)
 
+    for config in configs["ios"]["python_configurations"]:
+        all_versions.update_config(config)
+
     result_toml = dump_python_configurations(configs)
 
     rich.print()  # spacer
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index fa3245741..e16adca9c 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -15,6 +15,7 @@
 from typing import Any, Protocol, TextIO, assert_never
 
 import cibuildwheel
+import cibuildwheel.ios
 import cibuildwheel.linux
 import cibuildwheel.macos
 import cibuildwheel.pyodide
@@ -93,13 +94,14 @@ def main_inner(global_options: GlobalOptions) -> None:
 
     parser.add_argument(
         "--platform",
-        choices=["auto", "linux", "macos", "windows", "pyodide"],
+        choices=["auto", "linux", "macos", "windows", "pyodide", "ios"],
         default=None,
         help="""
-            Platform to build for. Use this option to override the
-            auto-detected platform. Specifying "macos" or "windows" only works
-            on that operating system, but "linux" works on all three, as long
-            as Docker/Podman is installed. Default: auto.
+            Platform to build for. Use this option to override the auto-detected
+            platform. Specifying "macos" or "windows" only works on that
+            operating system. "linux" works on any desktop OS, as long as
+            Docker/Podman is installed. "pyodide" only works on linux and macOS.
+            "ios" only work on macOS. Default: auto.
         """,
     )
 
@@ -240,6 +242,8 @@ def _compute_platform_only(only: str) -> PlatformName:
         return "windows"
     if "pyodide_" in only:
         return "pyodide"
+    if "ios_" in only:
+        return "ios"
     msg = f"Invalid --only='{only}', must be a build selector with a known platform"
     raise errors.ConfigurationError(msg)
 
@@ -301,6 +305,8 @@ def get_platform_module(platform: PlatformName) -> PlatformModule:
         return cibuildwheel.macos
     if platform == "pyodide":
         return cibuildwheel.pyodide
+    if platform == "ios":
+        return cibuildwheel.ios
     assert_never(platform)
 
 
diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py
index 7af966ade..de17c75dd 100644
--- a/cibuildwheel/architecture.py
+++ b/cibuildwheel/architecture.py
@@ -17,6 +17,7 @@
     "macos": "macOS",
     "windows": "Windows",
     "pyodide": "Pyodide",
+    "ios": "iOS",
 }
 
 ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
@@ -63,6 +64,12 @@ class Architecture(StrEnum):
     # WebAssembly
     wasm32 = auto()
 
+    # iOS "multiarch" architectures that include both
+    # the CPU architecture and the ABI.
+    arm64_iphoneos = auto()
+    arm64_iphonesimulator = auto()
+    x86_64_iphonesimulator = auto()
+
     @staticmethod
     def parse_config(config: str, platform: PlatformName) -> "set[Architecture]":
         result = set()
@@ -89,8 +96,8 @@ def parse_config(config: str, platform: PlatformName) -> "set[Architecture]":
 
     @staticmethod
     def native_arch(platform: PlatformName) -> "Architecture | None":
-        if platform == "pyodide":
-            return Architecture.wasm32
+        native_machine = platform_module.machine()
+        native_architecture = Architecture(native_machine)
 
         # Cross-platform support. Used for --print-build-identifiers or docker builds.
         host_platform: PlatformName = (
@@ -99,8 +106,18 @@ def native_arch(platform: PlatformName) -> "Architecture | None":
             else ("macos" if sys.platform.startswith("darwin") else "linux")
         )
 
-        native_machine = platform_module.machine()
-        native_architecture = Architecture(native_machine)
+        if platform == "pyodide":
+            return Architecture.wasm32
+        elif platform == "ios":
+            # Can only build for iOS on macOS. The "native" architecture is the
+            # simulator for the macOS native platform.
+            if host_platform == "macos":
+                if native_architecture == Architecture.x86_64:
+                    return Architecture.x86_64_iphonesimulator
+                else:
+                    return Architecture.arm64_iphonesimulator
+            else:
+                return None
 
         # we might need to rename the native arch to the machine we're running
         # on, as the same arch can have different names on different platforms
@@ -131,9 +148,13 @@ def auto_archs(platform: PlatformName) -> "set[Architecture]":
             elif Architecture.aarch64 in result and _check_aarch32_el0():
                 result.add(Architecture.armv7l)
 
-        if platform == "windows" and Architecture.AMD64 in result:
+        elif platform == "windows" and Architecture.AMD64 in result:
             result.add(Architecture.x86)
 
+        elif platform == "ios" and native_arch == Architecture.arm64_iphonesimulator:
+            # Also build the device wheel if we're on ARM64.
+            result.add(Architecture.arm64_iphoneos)
+
         return result
 
     @staticmethod
@@ -150,6 +171,11 @@ def all_archs(platform: PlatformName) -> "set[Architecture]":
             "macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
             "windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
             "pyodide": {Architecture.wasm32},
+            "ios": {
+                Architecture.x86_64_iphonesimulator,
+                Architecture.arm64_iphonesimulator,
+                Architecture.arm64_iphoneos,
+            },
         }
         return all_archs_map[platform]
 
diff --git a/cibuildwheel/ios.py b/cibuildwheel/ios.py
new file mode 100644
index 000000000..e3b6c0a47
--- /dev/null
+++ b/cibuildwheel/ios.py
@@ -0,0 +1,586 @@
+from __future__ import annotations
+
+import os
+import shlex
+import shutil
+import subprocess
+import sys
+from collections.abc import Sequence, Set
+from dataclasses import dataclass
+from pathlib import Path
+from typing import assert_never
+
+from filelock import FileLock
+
+from . import errors
+from .architecture import Architecture
+from .environment import ParsedEnvironment
+from .frontend import (
+    BuildFrontendConfig,
+    BuildFrontendName,
+    get_build_frontend_extra_flags,
+)
+from .logger import log
+from .macos import install_cpython as install_build_cpython
+from .options import Options
+from .selector import BuildSelector
+from .typing import PathOrStr
+from .util import resources
+from .util.cmd import call, shell
+from .util.file import (
+    CIBW_CACHE_PATH,
+    copy_test_sources,
+    download,
+    move_file,
+)
+from .util.helpers import prepare_command
+from .util.packaging import (
+    combine_constraints,
+    find_compatible_wheel,
+    get_pip_version,
+)
+from .venv import virtualenv
+
+
+@dataclass(frozen=True)
+class PythonConfiguration:
+    version: str
+    identifier: str
+    url: str
+    build_url: str
+
+    @property
+    def sdk(self) -> str:
+        return self.multiarch.rsplit("-", 1)[1]
+
+    @property
+    def arch(self) -> str:
+        return self.multiarch.rsplit("-", 1)[0]
+
+    @property
+    def multiarch(self) -> str:
+        # The multiarch identifier, as reported by `sys.implementation._multiarch`
+        return "-".join(self.identifier.split("-ios_")[1].rsplit("_", 1))
+
+    @property
+    def is_simulator(self) -> bool:
+        return self.identifier.endswith("_iphonesimulator")
+
+    @property
+    def xcframework_slice(self) -> str:
+        "XCframeworks include binaries for multiple ABIs; which ABI section should be used?"
+        return "ios-arm64_x86_64-simulator" if self.is_simulator else "ios-arm64"
+
+
+def get_python_configurations(
+    build_selector: BuildSelector,
+    architectures: Set[Architecture],
+) -> list[PythonConfiguration]:
+    # iOS builds are always cross builds; we need to install a macOS Python as
+    # well. Rather than duplicate the location of the URL of macOS installers,
+    # load the macos configurations, determine the macOS configuration that
+    # matches the platform we're building, and embed that URL in the parsed iOS
+    # configuration.
+    macos_python_configs = resources.read_python_configs("macos")
+
+    def build_url(config_dict: dict[str, str]) -> str:
+        # The iOS identifier will be something like cp313-ios_arm64_iphoneos.
+        # Drop the iphoneos suffix, then replace ios with macosx to yield
+        # cp313-macosx_arm64, which will be a macOS build identifier.
+        modified_ios_identifier = config_dict["identifier"].rsplit("_", 1)[0]
+        macos_identifier = modified_ios_identifier.replace("ios", "macosx")
+        matching = [
+            config for config in macos_python_configs if config["identifier"] == macos_identifier
+        ]
+        return matching[0]["url"]
+
+    # Load the platform configuration
+    full_python_configs = resources.read_python_configs("ios")
+    # Build the configurations, annotating with macOS URL details.
+    python_configurations = [
+        PythonConfiguration(
+            **item,
+            build_url=build_url(item),
+        )
+        for item in full_python_configs
+    ]
+
+    # Filter out configs that don't match any of the selected architectures
+    python_configurations = [
+        c
+        for c in python_configurations
+        if any(c.identifier.endswith(f"-ios_{a.value}") for a in architectures)
+    ]
+
+    # Skip builds as required by BUILD/SKIP
+    python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
+
+    return python_configurations
+
+
+def install_target_cpython(tmp: Path, config: PythonConfiguration, free_threading: bool) -> Path:
+    if free_threading:
+        msg = "Free threading builds aren't available for iOS (yet)"
+        raise errors.FatalError(msg)
+
+    # Install an iOS build of CPython
+    ios_python_tar_gz = config.url.rsplit("/", 1)[-1]
+    extension = ".tar.gz"
+    assert ios_python_tar_gz.endswith(extension)
+    installation_path = CIBW_CACHE_PATH / ios_python_tar_gz[: -len(extension)]
+    with FileLock(str(installation_path) + ".lock"):
+        if not installation_path.exists():
+            downloaded_tar_gz = tmp / ios_python_tar_gz
+            download(config.url, downloaded_tar_gz)
+            installation_path.mkdir(parents=True, exist_ok=True)
+            call("tar", "-C", installation_path, "-xf", downloaded_tar_gz)
+            downloaded_tar_gz.unlink()
+
+    return installation_path
+
+
+def cross_virtualenv(
+    *,
+    py_version: str,
+    target_python: Path,
+    multiarch: str,
+    build_python: Path,
+    venv_path: Path,
+    dependency_constraint_flags: Sequence[PathOrStr],
+) -> dict[str, str]:
+    """Create a cross-compilation virtual environment.
+
+    In a cross-compilation environment, the *target* is the platform where the
+    code will ultimately run, and the *build* is the platform where you're
+    running the compilation. When building iOS wheels, iOS is the target machine
+    and macOS is the build machine. The terminology around these machines varies
+    between build tools (configure uses "host" and "build"; cmake uses "target" and
+    "build host").
+
+    A cross-compilation virtualenv is an environment that is based on the
+    *build* python (so that binaries can execute); but it modifies the
+    environment at startup so that any request for platform details (such as
+    `sys.platform` or `sysconfig.get_platform()`) return details of the target
+    platform. It also applies a loader patch so that any virtualenv created by
+    the cross-compilation environment will also be a cross-compilation
+    environment.
+
+    :param py_version: The Python version (major.minor) in use
+    :param target_python: The path to the python binary for the target platform
+    :param multiarch: The multiarch tag for the target platform (i.e., the value
+        of `sys.implementation._multiarch`)
+    :param build_python: The path to the python binary for the build platform
+    :param venv_path: The path where the cross virtual environment should be
+        created.
+    :param dependency_constraint_flags: Any flags that should be used when
+        constraining dependencies in the environment.
+    """
+    # Create an initial macOS virtual environment
+    env = virtualenv(
+        py_version,
+        build_python,
+        venv_path,
+        dependency_constraint_flags,
+        use_uv=False,
+    )
+
+    # Convert the macOS virtual environment into an iOS virtual environment
+    # using the cross-platform conversion script in the iOS distribution.
+
+    # target_python is the path to the Python binary;
+    # determine the root of the XCframework slice that is being used.
+    slice_path = target_python.parent.parent
+    call(
+        "python",
+        str(slice_path / f"platform-config/{multiarch}/make_cross_venv.py"),
+        str(venv_path),
+        env=env,
+        cwd=venv_path,
+    )
+
+    # When running on macOS, it's easy for the build environment to leak into
+    # the target environment, especially when building for ARM64 (because the
+    # build architecture is the same as the target architecture). The primary
+    # culprit for this is Homebrew libraries leaking in as dependencies for iOS
+    # libraries.
+    #
+    # To prevent problems, set the PATH to isolate the build environment from
+    # sources that could introduce incompatible binaries.
+    env["PATH"] = os.pathsep.join(
+        [
+            # The target python's binary directory
+            str(target_python.parent),
+            # The cross-platform environments binary directory
+            str(venv_path / "bin"),
+            # Cargo's binary directory (to allow for Rust compilation)
+            str(Path.home() / ".cargo" / "bin"),
+            # The bare minimum Apple system paths.
+            "/usr/bin",
+            "/bin",
+            "/usr/sbin",
+            "/sbin",
+            "/Library/Apple/usr/bin",
+        ]
+    )
+    # Also unset DYLD_LIBRARY_PATH to ensure that no macOS libraries will be
+    # found and linked.
+    env.pop("DYLD_LIBRARY_PATH", None)
+
+    return env
+
+
+def setup_python(
+    tmp: Path,
+    python_configuration: PythonConfiguration,
+    dependency_constraint_flags: Sequence[PathOrStr],
+    environment: ParsedEnvironment,
+    build_frontend: BuildFrontendName,
+) -> tuple[Path, dict[str, str]]:
+    if build_frontend == "build[uv]":
+        msg = "uv doesn't support iOS"
+        raise errors.FatalError(msg)
+
+    # An iOS environment requires 2 python installs - one for the build machine
+    # (macOS), and one for the target (iOS). We'll only ever interact with the
+    # *target* python, but the build Python needs to exist to act as the base
+    # for a cross venv.
+    tmp.mkdir()
+    implementation_id = python_configuration.identifier.split("-")[0]
+    log.step(f"Installing Build Python {implementation_id}...")
+    if implementation_id.startswith("cp"):
+        free_threading = "t-ios" in python_configuration.identifier
+        build_python = install_build_cpython(
+            tmp,
+            python_configuration.version,
+            python_configuration.build_url,
+            free_threading,
+        )
+    else:
+        msg = f"Unknown Python implementation: {implementation_id}"
+        raise errors.FatalError(msg)
+
+    assert build_python.exists(), (
+        f"{build_python.name} not found, has {list(build_python.parent.iterdir())}"
+    )
+
+    log.step(f"Installing Target Python {implementation_id}...")
+    target_install_path = install_target_cpython(tmp, python_configuration, free_threading)
+    target_python = (
+        target_install_path
+        / "Python.xcframework"
+        / python_configuration.xcframework_slice
+        / "bin"
+        / f"python{python_configuration.version}"
+    )
+
+    assert target_python.exists(), (
+        f"{target_python.name} not found, has {list(target_install_path.iterdir())}"
+    )
+
+    log.step("Creating cross build environment...")
+
+    venv_path = tmp / "venv"
+    env = cross_virtualenv(
+        py_version=python_configuration.version,
+        target_python=target_python,
+        multiarch=python_configuration.multiarch,
+        build_python=build_python,
+        venv_path=venv_path,
+        dependency_constraint_flags=dependency_constraint_flags,
+    )
+    venv_bin_path = venv_path / "bin"
+    assert venv_bin_path.exists()
+
+    # We version pip ourselves, so we don't care about pip version checking
+    env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
+
+    # upgrade pip to the version matching our constraints
+    # if necessary, reinstall it to ensure that it's available on PATH as 'pip'
+    pip = ["python", "-m", "pip"]
+    call(
+        *pip,
+        "install",
+        "--upgrade",
+        "pip",
+        *dependency_constraint_flags,
+        env=env,
+        cwd=venv_path,
+    )
+
+    # Apply our environment after pip is ready
+    env = environment.as_dictionary(prev_environment=env)
+
+    # Check what Python version we're on
+    which_python = call("which", "python", env=env, capture_stdout=True).strip()
+    print(which_python)
+    if which_python != str(venv_bin_path / "python"):
+        msg = (
+            "cibuildwheel: python available on PATH doesn't match our installed instance. "
+            "If you have modified PATH, ensure that you don't overwrite cibuildwheel's "
+            "entry or insert python above it."
+        )
+        raise errors.FatalError(msg)
+    call("python", "--version", env=env)
+
+    # Check what pip version we're on
+    assert (venv_bin_path / "pip").exists()
+    which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
+    print(which_pip)
+    if which_pip != str(venv_bin_path / "pip"):
+        msg = (
+            "cibuildwheel: pip available on PATH doesn't match our installed instance. "
+            "If you have modified PATH, ensure that you don't overwrite cibuildwheel's "
+            "entry or insert pip above it."
+        )
+        raise errors.FatalError(msg)
+    call("pip", "--version", env=env)
+
+    # Ensure that IPHONEOS_DEPLOYMENT_TARGET is set in the environment
+    env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
+
+    log.step("Installing build tools...")
+    if build_frontend == "pip":
+        # No additional build tools required
+        pass
+    elif build_frontend == "build":
+        call(
+            "pip",
+            "install",
+            "--upgrade",
+            "build[virtualenv]",
+            *dependency_constraint_flags,
+            env=env,
+        )
+    else:
+        assert_never(build_frontend)
+
+    return target_install_path, env
+
+
+def build(options: Options, tmp_path: Path) -> None:
+    if sys.platform != "darwin":
+        msg = "iOS binaries can only be built on macOS"
+        raise errors.FatalError(msg)
+
+    python_configurations = get_python_configurations(
+        build_selector=options.globals.build_selector,
+        architectures=options.globals.architectures,
+    )
+
+    if not python_configurations:
+        return
+
+    try:
+        before_all_options_identifier = python_configurations[0].identifier
+        before_all_options = options.build_options(before_all_options_identifier)
+
+        if before_all_options.before_all:
+            log.step("Running before_all...")
+            env = before_all_options.environment.as_dictionary(prev_environment=os.environ)
+            env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
+            before_all_prepared = prepare_command(
+                before_all_options.before_all,
+                project=".",
+                package=before_all_options.package_dir,
+            )
+            shell(before_all_prepared, env=env)
+
+        built_wheels: list[Path] = []
+
+        for config in python_configurations:
+            build_options = options.build_options(config.identifier)
+            build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
+            # uv doesn't support iOS
+            if build_frontend.name == "build[uv]":
+                msg = "uv doesn't support iOS"
+                raise errors.FatalError(msg)
+
+            log.build_start(config.identifier)
+
+            identifier_tmp_dir = tmp_path / config.identifier
+            identifier_tmp_dir.mkdir()
+            built_wheel_dir = identifier_tmp_dir / "built_wheel"
+
+            constraints_path = build_options.dependency_constraints.get_for_python_version(
+                version=config.version, tmp_dir=identifier_tmp_dir
+            )
+            dependency_constraint_flags: Sequence[PathOrStr] = (
+                ["-c", constraints_path] if constraints_path else []
+            )
+
+            target_install_path, env = setup_python(
+                identifier_tmp_dir / "build",
+                config,
+                dependency_constraint_flags,
+                build_options.environment,
+                build_frontend.name,
+            )
+            pip_version = get_pip_version(env)
+
+            compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
+            if compatible_wheel:
+                log.step_end()
+                print(
+                    f"\nFound previously built wheel {compatible_wheel.name} "
+                    f"that is compatible with {config.identifier}. "
+                    "Skipping build step..."
+                )
+                test_wheel = compatible_wheel
+            else:
+                if build_options.before_build:
+                    log.step("Running before_build...")
+                    before_build_prepared = prepare_command(
+                        build_options.before_build,
+                        project=".",
+                        package=build_options.package_dir,
+                    )
+                    shell(before_build_prepared, env=env)
+
+                log.step("Building wheel...")
+                built_wheel_dir.mkdir()
+
+                extra_flags = get_build_frontend_extra_flags(
+                    build_frontend, build_options.build_verbosity, build_options.config_settings
+                )
+
+                build_env = env.copy()
+                build_env["VIRTUALENV_PIP"] = pip_version
+                if constraints_path:
+                    combine_constraints(build_env, constraints_path, None)
+
+                if build_frontend.name == "pip":
+                    # Path.resolve() is needed. Without it pip wheel may try to
+                    # fetch package from pypi.org. See
+                    # https://github.com/pypa/cibuildwheel/pull/369
+                    call(
+                        "python",
+                        "-m",
+                        "pip",
+                        "wheel",
+                        build_options.package_dir.resolve(),
+                        f"--wheel-dir={built_wheel_dir}",
+                        "--no-deps",
+                        *extra_flags,
+                        env=build_env,
+                    )
+                elif build_frontend.name == "build":
+                    call(
+                        "python",
+                        "-m",
+                        "build",
+                        build_options.package_dir,
+                        "--wheel",
+                        f"--outdir={built_wheel_dir}",
+                        *extra_flags,
+                        env=build_env,
+                    )
+                else:
+                    assert_never(build_frontend)
+
+                test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl"))
+
+                if built_wheel.name.endswith("none-any.whl"):
+                    raise errors.NonPlatformWheelError()
+
+                log.step_end()
+
+            if build_options.test_command and build_options.test_selector(config.identifier):
+                if not config.is_simulator:
+                    log.step("Skipping tests on non-simulator SDK")
+                elif config.arch != os.uname().machine:
+                    log.step("Skipping tests on non-native simulator architecture")
+                else:
+                    if build_options.before_test:
+                        before_test_prepared = prepare_command(
+                            build_options.before_test,
+                            project=".",
+                            package=build_options.package_dir,
+                        )
+                        shell(before_test_prepared, env=env)
+
+                    log.step("Setting up test harness...")
+                    # Clone the testbed project into the build directory
+                    testbed_path = identifier_tmp_dir / "testbed"
+                    call(
+                        "python",
+                        target_install_path / "testbed",
+                        "clone",
+                        testbed_path,
+                        env=build_env,
+                    )
+
+                    if not build_options.test_sources:
+                        # iOS requires an explicit test-sources, as the project directory
+                        # isn't visible on the simulator.
+
+                        msg = "Testing on iOS requires a definition of test-sources."
+                        raise errors.FatalError(msg)
+
+                    # Copy the test sources to the testbed app
+                    copy_test_sources(
+                        build_options.test_sources,
+                        build_options.package_dir,
+                        testbed_path / "iOSTestbed" / "app",
+                    )
+
+                    log.step("Installing test requirements...")
+                    # Install the compiled wheel (with any test extras), plus
+                    # the test requirements. Use the --platform tag to force
+                    # the installation of iOS wheels; this requires the use of
+                    # --only-binary=:all:
+                    ios_version = build_env["IPHONEOS_DEPLOYMENT_TARGET"]
+                    platform_tag = f"ios_{ios_version.replace('.', '_')}_{config.arch}_{config.sdk}"
+
+                    call(
+                        "python",
+                        "-m",
+                        "pip",
+                        "install",
+                        "--only-binary=:all:",
+                        "--platform",
+                        platform_tag,
+                        "--target",
+                        testbed_path / "iOSTestbed" / "app_packages",
+                        f"{test_wheel}{build_options.test_extras}",
+                        *build_options.test_requires,
+                        env=build_env,
+                    )
+
+                    log.step("Running test suite...")
+                    try:
+                        call(
+                            "python",
+                            testbed_path,
+                            "run",
+                            *(["--verbose"] if build_options.build_verbosity > 0 else []),
+                            "--",
+                            *(shlex.split(build_options.test_command)),
+                            env=build_env,
+                        )
+                        failed = False
+                    except subprocess.CalledProcessError:
+                        failed = True
+
+                    log.step_end(success=not failed)
+
+                    if failed:
+                        log.error(f"Test suite failed on {config.identifier}")
+                        sys.exit(1)
+
+            # We're all done here; move it to output (overwrite existing)
+            if compatible_wheel is None:
+                output_wheel = build_options.output_dir.joinpath(built_wheel.name)
+                moved_wheel = move_file(built_wheel, output_wheel)
+                if moved_wheel != output_wheel.resolve():
+                    log.warning(
+                        f"{built_wheel} was moved to {moved_wheel} instead of {output_wheel}"
+                    )
+                built_wheels.append(output_wheel)
+
+            # Clean up
+            shutil.rmtree(identifier_tmp_dir)
+
+            log.build_end()
+    except subprocess.CalledProcessError as error:
+        msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
+        raise errors.FatalError(msg) from error
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index ec50da8ab..391786a04 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -35,6 +35,9 @@
     "macosx_universal2": "macOS Universal 2 - x86_64 and arm64",
     "macosx_arm64": "macOS arm64 - Apple Silicon",
     "pyodide_wasm32": "Pyodide",
+    "ios_arm64_iphoneos": "iOS Device (ARM64)",
+    "ios_arm64_iphonesimulator": "iOS Simulator (ARM64)",
+    "ios_x86_64_iphonesimulator": "iOS Simulator (x86_64)",
 }
 
 
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index e3078f991..b5fa071a2 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -163,3 +163,10 @@ python_configurations = [
 python_configurations = [
   { identifier = "cp312-pyodide_wasm32", version = "3.12", pyodide_version = "0.27.0", pyodide_build_version = "0.29.2", emscripten_version = "3.1.58", node_version = "v20" },
 ]
+
+[ios]
+python_configurations = [
+  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b5/Python-3.13-iOS-support.b5.tar.gz" },
+  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b5/Python-3.13-iOS-support.b5.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b5/Python-3.13-iOS-support.b5.tar.gz" },
+]
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 2a811d2fe..4a84606cf 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -956,6 +956,51 @@
           "$ref": "#/properties/test-requires"
         }
       }
+    },
+    "ios": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "archs": {
+          "$ref": "#/properties/archs"
+        },
+        "before-all": {
+          "$ref": "#/properties/before-all"
+        },
+        "before-build": {
+          "$ref": "#/properties/before-build"
+        },
+        "before-test": {
+          "$ref": "#/properties/before-test"
+        },
+        "build-frontend": {
+          "$ref": "#/properties/build-frontend"
+        },
+        "build-verbosity": {
+          "$ref": "#/properties/build-verbosity"
+        },
+        "config-settings": {
+          "$ref": "#/properties/config-settings"
+        },
+        "dependency-versions": {
+          "$ref": "#/properties/dependency-versions"
+        },
+        "environment": {
+          "$ref": "#/properties/environment"
+        },
+        "repair-wheel-command": {
+          "$ref": "#/properties/repair-wheel-command"
+        },
+        "test-command": {
+          "$ref": "#/properties/test-command"
+        },
+        "test-extras": {
+          "$ref": "#/properties/test-extras"
+        },
+        "test-requires": {
+          "$ref": "#/properties/test-requires"
+        }
+       }
     }
   }
 }
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index b8e86c731..6b65e6369 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -51,4 +51,6 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest
 
 [tool.cibuildwheel.windows]
 
+[tool.cibuildwheel.ios]
+
 [tool.cibuildwheel.pyodide]
diff --git a/cibuildwheel/typing.py b/cibuildwheel/typing.py
index 87923c1e5..420ef05f6 100644
--- a/cibuildwheel/typing.py
+++ b/cibuildwheel/typing.py
@@ -12,7 +12,7 @@
 PathOrStr = str | os.PathLike[str]
 
 
-PlatformName = Literal["linux", "macos", "windows", "pyodide"]
+PlatformName = Literal["linux", "macos", "windows", "pyodide", "ios"]
 PLATFORMS: Final[frozenset[PlatformName]] = frozenset(typing.get_args(PlatformName))
 
 
diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py
index 2c8795b8d..f6619dd92 100644
--- a/cibuildwheel/util/packaging.py
+++ b/cibuildwheel/util/packaging.py
@@ -134,7 +134,7 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
     specified by `identifier` that is previously built.
     """
 
-    interpreter, platform = identifier.split("-")
+    interpreter, platform = identifier.split("-", 1)
     free_threaded = interpreter.endswith("t")
     if free_threaded:
         interpreter = interpreter[:-1]
@@ -157,8 +157,9 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
                 # If a minor version number is given, it has to be lower than the current one.
                 continue
 
-            if platform.startswith(("manylinux", "musllinux", "macosx")):
-                # Linux, macOS require the beginning and ending match (macos/manylinux version doesn't need to)
+            if platform.startswith(("manylinux", "musllinux", "macosx", "ios")):
+                # Linux, macOS, and iOS require the beginning and ending match
+                # (macos/manylinux/iOS version number doesn't need to match)
                 os_, arch = platform.split("_", 1)
                 if not tag.platform.startswith(os_):
                     continue
diff --git a/docs/contributing.md b/docs/contributing.md
index 237708999..4eea79599 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -36,7 +36,7 @@ So, if we can, I'd like to improve the experience on errors as well. In [this](h
 
 ### Running the tests
 
-When making a change to the codebase, you can run tests locally for quicker feedback than the CI runs on a PR. You can [run them directly](#making-a-venv), but the easiest way to run tests is using [nox](https://nox.thea.codes/).
+When making a change to the codebase, you can run tests locally for quicker feedback than the CI runs on a PR. You can run them directly, but the easiest way to run tests is using [nox](https://nox.thea.codes/).
 
 You can run all the tests locally by doing:
 
diff --git a/docs/options.md b/docs/options.md
index 0dfaf0a72..080c66bb8 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -247,7 +247,7 @@ environment variables will completely override any TOML configuration.
 
 > Override the auto-detected target platform
 
-Options: `auto` `linux` `macos` `windows` `pyodide`
+Options: `auto` `linux` `macos` `windows` `ios` `pyodide`
 
 Default: `auto`
 
@@ -255,6 +255,7 @@ Default: `auto`
 
 - For `linux`, you need [Docker or Podman](#container-engine) running, on Linux, macOS, or Windows.
 - For `macos` and `windows`, you need to be running on the respective system, with a working compiler toolchain installed - Xcode Command Line tools for macOS, and MSVC for Windows.
+- For `ios` you need to be running on macOS, with Xcode and the iOS simulator installed.
 - For `pyodide` you need to be on an x86-64 linux runner and `python3.12` must be available in `PATH`.
 
 This option can also be set using the [command-line option](#command-line) `--platform`. This option is not available in the `pyproject.toml` config.
@@ -288,34 +289,22 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
 
 <div class="build-id-table-marker"></div>
 
-|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                           |
-|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l         |
-| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l         |
-| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l |
-| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l |
-| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l                             |
-| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l |
-| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                |
-| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                |
-| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                               |
-| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                               |
+|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                           | iOS                                                                                               |
+|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
+| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l         |                                                                                                   |
+| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l         |                                                                                                   |
+| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l |                                                                                                   |
+| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l |                                                                                                   |
+| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l                             |                                                                                                   |
+| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator |
+| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                |                                                                                                   |
+| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                |                                                                                                   |
+| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                               |                                                                                                   |
+| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                               |                                                                                                   |
 
 The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
 The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
 
-The lowest value you can set `MACOSX_DEPLOYMENT_TARGET` is as follows:
-
-| Arch  | Python version range | Minimum target |
-|-------|----------------------|----------------|
-| Intel | CPython 3.8-3.11     | 10.9           |
-| Intel | CPython 3.12+        | 10.13          |
-| AS    | CPython or PyPy      | 11             |
-| Intel | PyPy 3.8             | 10.13          |
-| Intel | PyPy 3.9+            | 10.15          |
-
-If you set the value lower, cibuildwheel will cap it to the lowest supported value for each target as needed.
-
 Windows arm64 platform support is experimental.
 
 For an experimental WebAssembly build with `--platform pyodide`,
@@ -435,6 +424,7 @@ Options:
 - macOS: `x86_64` `arm64` `universal2`
 - Windows: `AMD64` `x86` `ARM64`
 - Pyodide: `wasm32`
+- iOS: `arm64_iphoneos` `arm64_iphonesimulator` `x86_64_iphonesimulator`
 - `auto`: The default archs for your machine - see the table below.
     - `auto64`: Just the 64-bit auto archs
     - `auto32`: Just the 32-bit auto archs
@@ -452,6 +442,8 @@ Default: `auto`
 | Windows / ARM64 | `ARM64` | `ARM64` | `ARM64` | |
 | macOS / Intel | `x86_64` | `x86_64` | `x86_64` |  |
 | macOS / Apple Silicon | `arm64` | `arm64` | `arm64` |  |
+| iOS on macOS / Intel | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` |  |
+| iOS on macOS / Apple Silicon | `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` |  |
 
 If not listed above, `auto` is the same as `native`.
 
@@ -459,7 +451,7 @@ If not listed above, `auto` is the same as `native`.
 [binfmt]: https://hub.docker.com/r/tonistiigi/binfmt
 
 Platform-specific environment variables are also available:<br/>
- `CIBW_ARCHS_MACOS` | `CIBW_ARCHS_WINDOWS` | `CIBW_ARCHS_LINUX`
+ `CIBW_ARCHS_MACOS` | `CIBW_ARCHS_WINDOWS` | `CIBW_ARCHS_LINUX` | `CIBW_ARCHS_IOS`
 
 This option can also be set using the [command-line option](#command-line)
 `--archs`. This option cannot be set in an `overrides` section in `pyproject.toml`.
@@ -674,8 +666,8 @@ possible, both through `--installer=uv` passed to build, as well as when making
 all build and test environments. This will generally speed up cibuildwheel.
 Make sure you have an external uv on Windows and macOS, either by
 pre-installing it, or installing cibuildwheel with the uv extra,
-`cibuildwheel[uv]`. You cannot use uv currently on Windows for ARM or for
-musllinux on s390x as binaries are not provided by uv. Legacy dependencies like
+`cibuildwheel[uv]`. You cannot use uv currently on Windows for ARM, for
+musllinux on s390x, or for iOS, as binaries are not provided by uv. Legacy dependencies like
 setuptools on Python < 3.12 and pip are not installed if using uv.
 
 Pyodide ignores this setting, as only "build" is supported.
@@ -752,7 +744,7 @@ a table of items, including arrays.
     single values.
 
 Platform-specific environment variables also available:<br/>
-`CIBW_CONFIG_SETTINGS_MACOS` | `CIBW_CONFIG_SETTINGS_WINDOWS` | `CIBW_CONFIG_SETTINGS_LINUX` | `CIBW_CONFIG_SETTINGS_PYODIDE`
+`CIBW_CONFIG_SETTINGS_MACOS` | `CIBW_CONFIG_SETTINGS_WINDOWS` | `CIBW_CONFIG_SETTINGS_LINUX` | `CIBW_CONFIG_SETTINGS_IOS` | `CIBW_CONFIG_SETTINGS_PYODIDE`
 
 
 #### Examples
@@ -783,7 +775,7 @@ You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to
 To specify more than one environment variable, separate the assignments by spaces.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX` | `CIBW_ENVIRONMENT_PYODIDE`
+`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX` | `CIBW_ENVIRONMENT_IOS` | `CIBW_ENVIRONMENT_PYODIDE`
 
 #### Examples
 
@@ -915,7 +907,7 @@ On linux, overriding it triggers a new container launch. It cannot be overridden
 on macOS and Windows.
 
 Platform-specific environment variables also available:<br/>
-`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX` | `CIBW_BEFORE_ALL_PYODIDE`
+`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX` | `CIBW_BEFORE_ALL_IOS` | `CIBW_BEFORE_ALL_PYODIDE`
 
 !!! note
 
@@ -980,7 +972,7 @@ The active Python binary can be accessed using `python`, and pip with `pip`; cib
 The command is run in a shell, so you can write things like `cmd1 && cmd2`.
 
 Platform-specific environment variables are also available:<br/>
- `CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX` | `CIBW_BEFORE_BUILD_PYODIDE`
+ `CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX` | `CIBW_BEFORE_BUILD_IOS` | `CIBW_BEFORE_BUILD_PYODIDE`
 
 #### Examples
 
@@ -1061,6 +1053,7 @@ Default:
 - on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
 - on macOS: `'delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}'`
 - on Windows: `''`
+- on iOS: `''`
 - on Pyodide: `''`
 
 A shell command to repair a built wheel by copying external library dependencies into the wheel tree and relinking them.
@@ -1075,7 +1068,7 @@ The following placeholders must be used inside the command and will be replaced
 The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX` | `CIBW_REPAIR_WHEEL_COMMAND_PYODIDE`
+`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX` | `CIBW_REPAIR_WHEEL_COMMAND_IOS` | `CIBW_REPAIR_WHEEL_COMMAND_PYODIDE`
 
 !!! tip
     cibuildwheel doesn't yet ship a default repair command for Windows.
@@ -1385,7 +1378,7 @@ specifiers inline with the `packages: SPECIFIER...` syntax.
     `./constraints.txt` if that's not found.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS` | `CIBW_DEPENDENCY_VERSIONS_PYODIDE`
+`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS` | `CIBW_DEPENDENCY_VERSIONS_IOS` | `CIBW_DEPENDENCY_VERSIONS_PYODIDE`
 
 !!! note
     This option does not affect the tools used on the Linux build - those versions
@@ -1442,7 +1435,7 @@ Platform-specific environment variables are also available:<br/>
 ## Testing
 
 ### `CIBW_TEST_COMMAND` {: #test-command}
-> Execute a shell command to test each built wheel
+> The command to test each built wheel
 
 Shell command to run tests after the build. The wheel will be installed
 automatically and available for import from the tests. If this variable is not
@@ -1461,10 +1454,12 @@ Alternatively, you can use the [`CIBW_TEST_SOURCES`](#test-sources) setting to
 create a temporary folder populated with a specific subset of project files to
 run your test suite.
 
-The command is run in a shell, so you can write things like `cmd1 && cmd2`.
+On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
+
+On iOS, the value of the `CIBW_TEST_COMMAND` setting is interpreted as the arguments to pass to `python -m` - that is, a Python module name, followed by arguments that will be assigned to `sys.argv`. Shell commands cannot be used.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_PYODIDE`
+`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE`
 
 #### Examples
 
@@ -1521,7 +1516,7 @@ The active Python binary can be accessed using `python`, and pip with `pip`; cib
 The command is run in a shell, so you can write things like `cmd1 && cmd2`.
 
 Platform-specific environment variables are also available:<br/>
- `CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX` | `CIBW_BEFORE_TEST_PYODIDE`
+ `CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX` | `CIBW_BEFORE_TEST_IOS` | `CIBW_BEFORE_TEST_PYODIDE`
 
 #### Examples
 
@@ -1583,8 +1578,13 @@ project, required for running the tests. If specified, these files and folders
 will be copied into a temporary folder, and that temporary folder will be used
 as the working directory for running the test suite.
 
+The use of `CIBW_TEST_SOURCES` is *required* for iOS builds. This is because the
+simulator does not have access to the project directory, as it is not stored on
+the simulator device. On iOS, the files will be copied into the test application,
+rather than a temporary folder.
+
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_SOURCES_MACOS` | `CIBW_TEST_SOURCES_WINDOWS` | `CIBW_TEST_SOURCES_LINUX` | `CIBW_TEST_SOURCES_PYODIDE`
+`CIBW_TEST_SOURCES_MACOS` | `CIBW_TEST_SOURCES_WINDOWS` | `CIBW_TEST_SOURCES_LINUX` | `CIBW_TEST_SOURCES_IOS` | `CIBW_TEST_SOURCES_PYODIDE`
 
 #### Examples
 
@@ -1612,7 +1612,7 @@ Platform-specific environment variables are also available:<br/>
 Space-separated list of dependencies required for running the tests.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX` | `CIBW_TEST_REQUIRES_PYODIDE`
+`CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX` | `CIBW_TEST_REQUIRES_IOS` | `CIBW_TEST_REQUIRES_PYODIDE`
 
 #### Examples
 
@@ -1652,7 +1652,7 @@ tests. This can be used to avoid having to redefine test dependencies in
 `setup.cfg` or `setup.py`.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX` | `CIBW_TEST_EXTRAS_PYODIDE`
+`CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX` | `CIBW_TEST_EXTRAS_IOS` | `CIBW_TEST_EXTRAS_PYODIDE`
 
 #### Examples
 
@@ -1779,7 +1779,7 @@ export CIBW_DEBUG_TRACEBACK=TRUE
 A number from 1 to 3 to increase the level of verbosity (corresponding to invoking pip with `-v`, `-vv`, and `-vvv`), between -1 and -3 (`-q`, `-qq`, and `-qqq`), or just 0 (default verbosity). These flags are useful while debugging a build when the output of the actual build invoked by `pip wheel` is required. Has no effect on the `build` backend, which produces verbose output by default.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX` | `CIBW_BUILD_VERBOSITY_PYODIDE`
+`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX` | `CIBW_BUILD_VERBOSITY_IOS` | `CIBW_BUILD_VERBOSITY_PYODIDE`
 
 #### Examples
 
diff --git a/docs/platforms/ios.md b/docs/platforms/ios.md
new file mode 100644
index 000000000..a58c0132b
--- /dev/null
+++ b/docs/platforms/ios.md
@@ -0,0 +1,68 @@
+---
+title: 'iOS'
+---
+
+# iOS builds
+
+## Pre-requisites
+
+You must be building on a macOS machine, with Xcode installed. The Xcode installation must have an iOS SDK available, with all license agreements agreed to by the user. To check if an iOS SDK is available, open the Xcode settings panel, and check the Platforms tab. This will also ensure that license agreements have been acknowledged.
+
+Building iOS wheels also requires a working macOS Python installation. See the notes on [macOS builds](./macos.md) for details about configuration of the macOS environment.
+
+## Specifying an iOS build
+
+iOS is effectively 2 platforms - physical devices, and simulators. While the API for these two platforms are identical, the ABI is not compatible, even when dealing with a device and simulator with the same CPU architecture. For this reason, the architecture specification for iOS builds includes *both* the CPU architecture *and* the ABI that is being targeted. There are three possible values for architecture on iOS; the values match those used by `sys.implementation._multiarch` when running on iOS (with hyphens replaced with underscores, matching wheel filename normalization):
+
+* `arm64_iphoneos` (for physical iOS devices);
+* `arm64_iphonesimulator` (for iOS simulators running on Apple Silicon macOS machines); and
+* `x64_64_iphonesimulator` (for iOS simulators running on Intel macOS machines).
+
+By default, cibuildwheel will build wheels for all three of these targets.
+
+If you need to specify different compilation flags or other properties on a per-ABI or per-CPU basis, you can use [configuration overrides](../../options/#overrides) with a `select` clause that targets the specific ABI or architecture. For example, consider the following example:
+
+```
+[tool.cibuildwheel.ios]
+test-sources = ["tests"]
+test-requires = ["pytest"]
+
+[[tool.cibuildwheel.overrides]]
+select = "*_iphoneos"
+environment.PATH = "/path/to/special/device/details:..."
+
+[[tool.cibuildwheel.overrides]]
+select = "*-ios_arm64_*"
+inherit.test-requires = "append"
+test-requires = ["arm64-testing-helper"]
+```
+
+This configuration would:
+
+ * Specify a `test-sources` and `test-requires` for all iOS targets;
+ * Add a `PATH` setting that will be used on physical iOS devices; and
+ * Add `arm64-testing-helper` to the test environment for all ARM64 iOS devices (whether simulator or device).
+
+## iOS version compatibility
+
+iOS builds will honor the `IPHONEOS_DEPLOYMENT_TARGET` environment variable to set the minimum supported API version for generated wheels. This will default to `13.0` if the environment variable isn't set.
+
+## Cross platform builds
+
+iOS builds are *cross platform builds*, as it not possible to run compilers and other build tools "on device". The pre-compiled iOS binaries used to support iOS builds include tooling that can convert any virtual environment into a cross platform virtual environment - that is, an environment that can run binaries on the build machine (macOS), but, if asked, will respond as if it is an iOS machine. This allows `pip`, `build`, and other build tools to perform iOS-appropriate behaviour.
+
+## Build frontend support
+
+iOS builds support both the `pip` and `build` build frontends. In principle, support for `uv` with the `build[uv]` frontend should be possible, but `uv` [doesn't currently have support for cross-platform builds](https://github.com/astral-sh/uv/issues/7957), and [doesn't have support for iOS (or Android) tags](https://github.com/astral-sh/uv/issues/8029).
+
+## Build environment
+
+The environment used to run builds does not inherit the full user environment - in particular, `PATH` is deliberately re-written. This is because UNIX C tooling doesn't do a great job differentiating between "macOS ARM64" and "iOS ARM64" binaries. If (for example) Homebrew is on the path when compilation commands are invoked, it's easy for a macOS version of a library to be linked into the iOS binary, rendering it unusable on iOS. To prevent this, iOS builds always force `PATH` to a "known minimal" path, that includes only the bare system utilities, plus the current user's cargo folder (to facilitate Rust builds).
+
+## Tests
+
+If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices.
+
+The iOS test environment can't support running shell scripts, so the [`CIBW_TEST_COMMAND`](../../options#test-command) value must be specified as if it were a command line being passed to `python -m ...`. In addition, the project must use [`CIBW_TEST_SOURCES`](../../options#test-sources) to specify the minimum subset of files that should be copied to the test environment. This is because the test must be run "on device", and the simulator device will not have access to the local project directory.
+
+The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <CIBW_TEST_COMMAND>`.
diff --git a/docs/platforms/linux.md b/docs/platforms/linux.md
new file mode 100644
index 000000000..ef86439c3
--- /dev/null
+++ b/docs/platforms/linux.md
@@ -0,0 +1,13 @@
+### Linux builds
+
+If you've got [Docker](https://www.docker.com/products/docker-desktop) installed on your development machine, you can run a Linux build.
+
+!!! tip
+    You can run the Linux build on any platform. Even Windows can run
+    Linux containers these days, but there are a few hoops to jump
+    through. Check [this document](https://docs.microsoft.com/en-us/virtualization/windowscontainers/quick-start/quick-start-windows-10-linux)
+    for more info.
+
+Because the builds are happening in manylinux Docker containers, they're perfectly reproducible.
+
+The only side effect to your system will be docker images being pulled.
diff --git a/docs/platforms/macos.md b/docs/platforms/macos.md
new file mode 100644
index 000000000..01e8d0ec8
--- /dev/null
+++ b/docs/platforms/macos.md
@@ -0,0 +1,46 @@
+---
+title: 'macOS'
+---
+
+# macOS builds
+
+## Pre-requisites
+
+Pre-requisite: you need to have native build tools installed.
+
+Because the builds are happening without full isolation, there might be some differences compared to CI builds (Xcode version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
+
+In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. `Cache folder: /Users/Matt/Library/Caches/cibuildwheel`).
+
+You can override the cache folder using the `CIBW_CACHE_PATH` environment variable.
+
+!!! warning
+    cibuildwheel uses official python.org macOS installers for CPython but those can only be installed globally.
+
+    In order not to mess with your system, cibuildwheel won't install those if they are missing. Instead, it will error out with a message to let you install the missing CPython:
+
+    ```console
+    Error: CPython 3.9 is not installed.
+    cibuildwheel will not perform system-wide installs when running outside of CI.
+    To build locally, install CPython 3.9 on this machine, or, disable this version of Python using CIBW_SKIP=cp39-macosx_*
+
+    Download link: https://www.python.org/ftp/python/3.9.8/python-3.9.8-macosx10.9.pkg
+    ```
+
+## macOS Version Compatibility
+
+macOS builds will honor the `MACOSX_DEPLOYMENT_TARGET` environment variable to control the minimum supported macOS version for generated wheels. The lowest value you can set `MACOSX_DEPLOYMENT_TARGET` is as follows:
+
+| Arch  | Python version range | Minimum target |
+|-------|----------------------|----------------|
+| Intel | CPython 3.8-3.11     | 10.9           |
+| Intel | CPython 3.12+        | 10.13          |
+| AS    | CPython or PyPy      | 11             |
+| Intel | PyPy 3.8             | 10.13          |
+| Intel | PyPy 3.9+            | 10.15          |
+
+If you set the value lower, cibuildwheel will cap it to the lowest supported value for each target as needed.
+
+## Universal builds
+
+By default, macOS builds will build a single architecture wheel, using the build machine's architecture. If you need to support both x86_64 and Apple Silicon, you can use the `CIBW_ARCHS` environment variable to specify the architectures you want to build, or the value `universal2` to build a multi-architecture wheel. cibuildwheel will test x86_64 wheels (or the x86_64 slice of a `universal2` wheel) when running on Apple Silicon hardware, but it is *not* possible to test Apple Silicon wheels on x86_64 hardware.
diff --git a/docs/platforms/pyodide.md b/docs/platforms/pyodide.md
new file mode 100644
index 000000000..a6edad681
--- /dev/null
+++ b/docs/platforms/pyodide.md
@@ -0,0 +1,13 @@
+---
+title: 'Pyodide'
+---
+
+# Pyodide (WebAssembly) builds (experimental)
+
+## Prerequisites
+
+You need to have a matching host version of Python (unlike all other cibuildwheel platforms). Linux host highly recommended; macOS hosts may work (e.g. invoking `pytest` directly in [`CIBW_TEST_COMMAND`](../options.md#test-command) is [currently failing](https://github.com/pyodide/pyodide/issues/4802)) and Windows hosts will not work.
+
+## Specifying a pyodide build
+
+You must target pyodide with `--platform pyodide` (or use `--only` on the identifier).
diff --git a/docs/platforms/windows.md b/docs/platforms/windows.md
new file mode 100644
index 000000000..421fa8543
--- /dev/null
+++ b/docs/platforms/windows.md
@@ -0,0 +1,15 @@
+---
+title: 'Windows'
+---
+
+# Windows builds
+
+## Pre-requisites
+
+You must have native build tools (i.e., Visual Studio) installed.
+
+Because the builds are happening without full isolation, there might be some differences compared to CI builds (Visual Studio version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
+
+In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. `Cache folder: C:\Users\Matt\AppData\Local\pypa\cibuildwheel\Cache`).
+
+You can override the cache folder using the ``CIBW_CACHE_PATH`` environment variable.
diff --git a/docs/setup.md b/docs/setup.md
index 5b534f1b8..e82f5a3e8 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -4,6 +4,16 @@ title: 'Setup'
 
 # Setup
 
+## Platform support
+
+Each platform that cibuildwheel supports has its own prerequisites and platform-specific behaviors. cibuildwheel supports the following platforms:
+
+* [Linux](./platforms/linux.md)
+* [Windows](./platforms/windows.md)
+* [macOS](./platforms/macos.md)
+* [iOS](./platforms/ios.md)
+* [Experimental: Pyodide (WebAssembly)](./platforms/pyodide.md)
+
 ## Run cibuildwheel locally (optional) {: #local}
 
 Before getting to CI setup, it can be convenient to test cibuildwheel
@@ -60,66 +70,11 @@ You should see the builds taking place. You can experiment with options using en
     cibuildwheel
     ```
 
-### Linux builds
-
-If you've got [Docker](https://www.docker.com/products/docker-desktop) installed on
-your development machine, you can run a Linux build.
-
-!!! tip
-    You can run the Linux build on any platform. Even Windows can run
-    Linux containers these days, but there are a few  hoops to jump
-    through. Check [this document](https://docs.microsoft.com/en-us/virtualization/windowscontainers/quick-start/quick-start-windows-10-linux)
-    for more info.
-
-Because the builds are happening in manylinux Docker containers,
-they're perfectly reproducible.
-
-The only side effect to your system will be docker images being pulled.
-
-### macOS / Windows builds
-
-Pre-requisite: you need to have native build tools installed.
-
-Because the builds are happening without full isolation, there might be some
-differences compared to CI builds (Xcode version, Visual Studio version,
-OS version, local files, ...) that might prevent you from finding an issue only
-seen in CI.
-
-In order to speed-up builds, cibuildwheel will cache the tools it needs to be
-reused for future builds. The folder used for caching is system/user dependent and is
-reported in the printed preamble of each run (e.g. "Cache folder: /Users/Matt/Library/Caches/cibuildwheel").
-
-You can override the cache folder using the ``CIBW_CACHE_PATH`` environment variable.
-
-!!! warning
-    cibuildwheel uses official python.org macOS installers for CPython but
-    those can only be installed globally.
-
-    In order not to mess with your system, cibuildwheel won't install those if they are
-    missing. Instead, it will error out with a message to let you install the missing
-    CPython:
-
-    ```console
-    Error: CPython 3.9 is not installed.
-    cibuildwheel will not perform system-wide installs when running outside of CI.
-    To build locally, install CPython 3.9 on this machine, or, disable this version of Python using CIBW_SKIP=cp39-macosx_*
-
-    Download link: https://www.python.org/ftp/python/3.9.8/python-3.9.8-macosx10.9.pkg
-    ```
-
-### Pyodide (WebAssembly) builds (experimental)
-
-Pre-requisite: you need to have a matching host version of Python (unlike all
-other cibuildwheel platforms). Linux host highly recommended; macOS hosts may
-work (e.g. invoking `pytest` directly in [`CIBW_TEST_COMMAND`](options.md#test-command) is [currently failing](https://github.com/pyodide/pyodide/issues/4802)) and Windows hosts will not work.
-
-You must target pyodide with `--platform pyodide` (or use `--only` on the identifier).
-
 ## Configure a CI service
 
 ### GitHub Actions [linux/mac/windows] {: #github-actions}
 
-To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/workflows/build_wheels.yml` file in your repo.
+To build Linux, macOS, and Windows wheels using GitHub Actions, create a `.github/workflows/build_wheels.yml` file in your repo.
 
 !!! tab "Action"
     For GitHub Actions, `cibuildwheel` provides an action you can use. This is
@@ -144,29 +99,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
     > .github/workflows/build_wheels.yml
 
     ```yaml
-    name: Build
-
-    on: [push, pull_request]
-
-    jobs:
-      build_wheels:
-        name: Build wheels on ${{ matrix.os }}
-        runs-on: ${{ matrix.os }}
-        strategy:
-          matrix:
-            # macos-13 is an intel runner, macos-14 is apple silicon
-            os: [ubuntu-latest, windows-latest, macos-13, macos-14]
-
-        steps:
-          - uses: actions/checkout@v4
-
-          - name: Build wheels
-            run: pipx run cibuildwheel==2.23.0
-
-          - uses: actions/upload-artifact@v4
-            with:
-              name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
-              path: ./wheelhouse/*.whl
+    {% include "../examples/github-pipx.yml" %}
     ```
 
 !!! tab "Generic"
@@ -176,45 +109,17 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
     appeal to you.
 
     > .github/workflows/build_wheels.yml
-
-    ```yaml
-    name: Build
-
-    on: [push, pull_request]
-
-    jobs:
-      build_wheels:
-        name: Build wheels on ${{ matrix.os }}
-        runs-on: ${{ matrix.os }}
-        strategy:
-          matrix:
-            # macos-13 is an intel runner, macos-14 is apple silicon
-            os: [ubuntu-latest, windows-latest, macos-13, macos-14]
-
-        steps:
-          - uses: actions/checkout@v4
-
-          # Used to host cibuildwheel
-          - uses: actions/setup-python@v5
-
-          - name: Install cibuildwheel
-            run: python -m pip install cibuildwheel==2.23.0
-
-          - name: Build wheels
-            run: python -m cibuildwheel --output-dir wheelhouse
-
-          - uses: actions/upload-artifact@v4
-            with:
-              name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
-              path: ./wheelhouse/*.whl
-    ```
-
+    {%
+       include-markdown "../README.md"
+       start="<!--generic-github-start-->"
+       end="<!--generic-github-end-->"
+    %}
 
 Commit this file, and push to GitHub - either to your default branch, or to a PR branch. The build should start automatically.
 
 For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
 
-[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
+[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example to include iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI.
 
 
 ### Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 2061be4d2..119b365b2 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -12,18 +12,47 @@ on:
 
 jobs:
   build_wheels:
-    name: Build wheels on ${{ matrix.os }}
-    runs-on: ${{ matrix.os }}
+    name: Build wheels for ${{ matrix.os }}
+    runs-on: ${{ matrix.runs-on }}
     strategy:
       matrix:
-        # macos-13 is an intel runner, macos-14 is apple silicon
-        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14]
+        os: [ linux-intel, linux-arm, windows, macOS-intel, macOS-arm, iOS, pyodide ]
+        include:
+          - archs: auto
+            platform: auto
+          - os: linux-intel
+            runs-on: ubuntu-latest
+          - os: linux-arm
+            runs-on: ubuntu-24.04-arm
+          - os: windows
+            runs-on: windows-latest
+          - os: macos-intel
+            # macos-13 was the last x86_64 runner
+            runs-on: macos-13
+          - os: macos-arm
+            # macos-14+ (including latest) are ARM64 runners
+            runs-on: macos-latest
+            archs: auto,universal2
+          - os: ios
+            runs-on: macos-latest
+            platform: ios
+          - os: pyodide
+            runs-on: ubuntu-latest
+            platform: pyodide
 
     steps:
       - uses: actions/checkout@v4
 
       - name: Build wheels
         uses: pypa/cibuildwheel@v2.23.0
+        env:
+          CIBW_PLATFORM: ${{ matrix.platform }}
+          CIBW_ARCHS: ${{ matrix.archs }}
+        # Can also be configured directly, using `with:`
+        # with:
+        #   package-dir: .
+        #   output-dir: wheelhouse
+        #   config-file: "{package}/pyproject.toml"
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 91c2a7edf..530ea05ce 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -18,7 +18,7 @@ jobs:
         uses: pypa/cibuildwheel@v2.23.0
         # env:
         #   CIBW_SOME_OPTION: value
-        #    ...
+        #   ...
         # with:
         #   package-dir: .
         #   output-dir: wheelhouse
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
new file mode 100644
index 000000000..0e365cc9c
--- /dev/null
+++ b/examples/github-pipx.yml
@@ -0,0 +1,23 @@
+name: Build
+
+on: [push, pull_request]
+
+jobs:
+  build_wheels:
+    name: Build wheels on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        # macos-13 is an intel runner, macos-14 is apple silicon
+        os: [ubuntu-latest, windows-latest, macos-13, macos-14]
+
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Build wheels
+        run: pipx run cibuildwheel==2.23.0
+
+      - uses: actions/upload-artifact@v4
+        with:
+          name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
+          path: ./wheelhouse/*.whl
diff --git a/mkdocs.yml b/mkdocs.yml
index 6749726dc..1d0150736 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -20,14 +20,22 @@ extra_javascript:
 
 nav:
   - Home: index.md
-  - setup.md
-  - options.md
-  - deliver-to-pypi.md
-  - cpp_standards.md
-  - faq.md
-  - working-examples.md
-  - contributing.md
-  - changelog.md
+  - User guide:
+    - setup.md
+    - options.md
+    - deliver-to-pypi.md
+    - cpp_standards.md
+    - faq.md
+    - working-examples.md
+  - Supported Platforms:
+    - platforms/linux.md
+    - platforms/windows.md
+    - platforms/macos.md
+    - platforms/ios.md
+    - platforms/pyodide.md
+  - About the project:
+    - contributing.md
+    - changelog.md
 
 markdown_extensions:
   - md_in_html
diff --git a/test/test_emulation.py b/test/test_emulation.py
index a8926c0bd..70242a510 100644
--- a/test/test_emulation.py
+++ b/test/test_emulation.py
@@ -11,8 +11,8 @@
 import spam
 
 def test_spam():
-    assert spam.system('python -c "exit(0)"') == 0
-    assert spam.system('python -c "exit(1)"') != 0
+    assert spam.filter("spam") == 0
+    assert spam.filter("ham") != 0
 """
 
 
diff --git a/test/test_ios.py b/test/test_ios.py
new file mode 100644
index 000000000..6dc7b40d3
--- /dev/null
+++ b/test/test_ios.py
@@ -0,0 +1,95 @@
+from __future__ import annotations
+
+import os
+import platform
+import subprocess
+
+import pytest
+
+from . import test_projects, utils
+
+basic_project = test_projects.new_c_project()
+basic_project.files["tests/test_platform.py"] = f"""
+import platform
+from unittest import TestCase
+
+class TestPlatform(TestCase):
+    def test_platform(self):
+        self.assertEqual(platform.machine(), "{platform.machine()}")
+
+"""
+
+
+# iOS tests shouldn't be run in parallel, because they're dependent on starting
+# a simulator. It's *possible* to start multiple simulators, but not advisable
+# to start as many simulators as there are CPUs on the test machine.
+@pytest.mark.xdist_group(name="ios")
+@pytest.mark.parametrize(
+    "build_config",
+    [
+        # Default to the pip build frontend
+        {"CIBW_PLATFORM": "ios"},
+        # Also check the build frontend
+        {"CIBW_PLATFORM": "ios", "CIBW_BUILD_FRONTEND": "build"},
+    ],
+)
+def test_ios_platforms(tmp_path, build_config):
+    if utils.platform != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+    basic_project.generate(project_dir)
+
+    actual_wheels = utils.cibuildwheel_run(
+        project_dir,
+        add_env={
+            "CIBW_BUILD": "cp313-*",
+            "CIBW_TEST_SOURCES": "tests",
+            "CIBW_TEST_COMMAND": "unittest discover tests test_platform.py",
+            **build_config,
+        },
+    )
+
+    ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_")
+    platform_machine = platform.machine()
+
+    # Tests are only executed on simulator. The test suite passes if it's
+    # running on the same architecture as the current platform.
+    if platform_machine == "x86_64":
+        expected_wheels = {
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl",
+        }
+
+    elif platform_machine == "arm64":
+        expected_wheels = {
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl",
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl",
+        }
+
+    assert set(actual_wheels) == expected_wheels
+
+
+@pytest.mark.xdist_group(name="ios")
+def test_no_test_sources(tmp_path, capfd):
+    if utils.platform != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+    basic_project.generate(project_dir)
+
+    with pytest.raises(subprocess.CalledProcessError):
+        utils.cibuildwheel_run(
+            project_dir,
+            add_env={
+                "CIBW_PLATFORM": "ios",
+                "CIBW_BUILD": "cp313-*",
+                "CIBW_TEST_COMMAND": "tests",
+            },
+        )
+
+    captured = capfd.readouterr()
+    assert "Testing on iOS requires a definition of test-sources." in captured.err
diff --git a/test/test_macos_archs.py b/test/test_macos_archs.py
index 52811f794..848303ec8 100644
--- a/test/test_macos_archs.py
+++ b/test/test_macos_archs.py
@@ -1,5 +1,4 @@
 import platform
-import subprocess
 
 import pytest
 
@@ -20,24 +19,10 @@
 DEPLOYMENT_TARGET_TOO_LOW_WARNING = "Bumping MACOSX_DEPLOYMENT_TARGET"
 
 
-def get_xcode_version() -> tuple[int, int]:
-    output = subprocess.run(
-        ["xcodebuild", "-version"],
-        text=True,
-        check=True,
-        stdout=subprocess.PIPE,
-    ).stdout
-    lines = output.splitlines()
-    _, version_str = lines[0].split()
-
-    version_parts = version_str.split(".")
-    return (int(version_parts[0]), int(version_parts[1]))
-
-
 def test_cross_compiled_build(tmp_path):
     if utils.platform != "macos":
         pytest.skip("this test is only relevant to macos")
-    if get_xcode_version() < (12, 2):
+    if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
 
     project_dir = tmp_path / "project"
@@ -71,7 +56,7 @@ def test_cross_compiled_build(tmp_path):
 def test_cross_compiled_test(tmp_path, capfd, build_universal2, test_config):
     if utils.platform != "macos":
         pytest.skip("this test is only relevant to macos")
-    if get_xcode_version() < (12, 2):
+    if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
 
     project_dir = tmp_path / "project"
@@ -153,7 +138,7 @@ def test_deployment_target_warning_is_firing(tmp_path, capfd):
 def test_universal2_testing_on_x86_64(tmp_path, capfd, skip_arm64_test):
     if utils.platform != "macos":
         pytest.skip("this test is only relevant to macos")
-    if get_xcode_version() < (12, 2):
+    if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
     if platform.machine() != "x86_64":
         pytest.skip("this test only works on x86_64")
@@ -223,7 +208,7 @@ def test_universal2_testing_on_arm64(build_frontend_env, tmp_path, capfd):
 def test_cp38_arm64_testing(tmp_path, capfd, request):
     if utils.platform != "macos":
         pytest.skip("this test is only relevant to macos")
-    if get_xcode_version() < (12, 2):
+    if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
     if platform.machine() != "arm64":
         pytest.skip("this test only works on arm64")
diff --git a/test/test_projects/c.py b/test/test_projects/c.py
index 05882ec1e..55d759875 100644
--- a/test/test_projects/c.py
+++ b/test/test_projects/c.py
@@ -8,15 +8,16 @@
 {{ spam_c_top_level_add }}
 
 static PyObject *
-spam_system(PyObject *self, PyObject *args)
+spam_filter(PyObject *self, PyObject *args)
 {
-    const char *command;
+    const char *content;
     int sts;
 
-    if (!PyArg_ParseTuple(args, "s", &command))
+    if (!PyArg_ParseTuple(args, "s", &content))
         return NULL;
 
-    sts = system(command);
+    // Spam should not be allowed through the filter.
+    sts = strcmp(content, "spam");
 
     {{ spam_c_function_add | indent(4) }}
 
@@ -25,7 +26,7 @@
 
 /* Module initialization */
 static PyMethodDef module_methods[] = {
-    {"system", (PyCFunction)spam_system, METH_VARARGS,
+    {"filter", (PyCFunction)spam_filter, METH_VARARGS,
      "Execute a shell command."},
     {NULL}  /* Sentinel */
 };
diff --git a/test/test_testing.py b/test/test_testing.py
index e35e7e0f3..4dfda9141 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -44,9 +44,9 @@ def path_contains(parent, child):
 
 
 class TestSpam(TestCase):
-    def test_system(self):
-        self.assertEqual(0, spam.system('python -c "exit(0)"'))
-        self.assertNotEqual(0, spam.system('python -c "exit(1)"'))
+    def test_filter(self):
+        self.assertEqual(0, spam.filter("spam"))
+        self.assertNotEqual(0, spam.filter("ham"))
 
     def test_virtualenv(self):
         # sys.prefix is different from sys.base_prefix when running a virtualenv
diff --git a/test/utils.py b/test/utils.py
index 8195e4923..921494e26 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -335,6 +335,21 @@ def get_macos_version() -> tuple[int, int]:
     return tuple(map(int, version_str.split(".")[:2]))  # type: ignore[return-value]
 
 
+def get_xcode_version() -> tuple[int, int]:
+    """Calls `xcodebuild -version` to retrieve the Xcode version as a 2-tuple."""
+    output = subprocess.run(
+        ["xcodebuild", "-version"],
+        text=True,
+        check=True,
+        stdout=subprocess.PIPE,
+    ).stdout
+    lines = output.splitlines()
+    _, version_str = lines[0].split()
+
+    version_parts = version_str.split(".")
+    return (int(version_parts[0]), int(version_parts[1]))
+
+
 def skip_if_pyodide(reason: str) -> Any:
     return pytest.mark.skipif(platform == "pyodide", reason=reason)
 

From 572f2a72b3ae3f94384e6559292aed3a1daa9000 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Wed, 12 Mar 2025 09:41:51 +0100
Subject: [PATCH 009/193] fix: image deprecation warning (#2314)

---
 cibuildwheel/options.py   | 34 ++++++++++++++++------------------
 unit_test/options_test.py | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 18 deletions(-)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index aff0566c2..fa7605343 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -672,6 +672,20 @@ def globals(self) -> GlobalOptions:
             allow_empty=allow_empty,
         )
 
+    def _check_pinned_image(self, value: str, pinned_images: Mapping[str, str]) -> None:
+        if (
+            value in {"manylinux1", "manylinux2010", "manylinux_2_24", "musllinux_1_1"}
+            and value not in self._image_warnings
+        ):
+            self._image_warnings.add(value)
+            msg = (
+                f"Deprecated image {value!r}. This value will not work"
+                " in a future version of cibuildwheel. Either upgrade to a supported"
+                " image or continue using the deprecated image by pinning directly"
+                f" to {pinned_images[value]!r}."
+            )
+            log.warning(msg)
+
     def _compute_build_options(self, identifier: str | None) -> BuildOptions:
         """
         Compute BuildOptions for a single run configuration. Normally accessed
@@ -778,24 +792,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                         # default to manylinux2014
                         image = pinned_images["manylinux2014"]
                     elif config_value in pinned_images:
-                        if (
-                            config_value
-                            in {
-                                "manylinux1",
-                                "manylinux2010",
-                                "manylinux_2_24",
-                                "musllinux_1_1",
-                            }
-                            and config_value not in self._image_warnings
-                        ):
-                            self._image_warnings.add(config_value)
-                            msg = (
-                                f"Deprecated image {config_value!r}. This value will not work"
-                                " in a future version of cibuildwheel. Either upgrade to a supported"
-                                " image or continue using the deprecated image by pinning directly"
-                                f" to {pinned_images[config_value]!r}."
-                            )
-                            log.warning(msg)
+                        self._check_pinned_image(config_value, pinned_images)
                         image = pinned_images[config_value]
                     else:
                         image = config_value
@@ -810,6 +807,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                     if not config_value:
                         image = pinned_images["musllinux_1_2"]
                     elif config_value in pinned_images:
+                        self._check_pinned_image(config_value, pinned_images)
                         image = pinned_images[config_value]
                     else:
                         image = config_value
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index 1bd67aa77..cbbdba596 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -529,3 +529,36 @@ def test_dependency_versions_toml(
         else:
             assert parsed_dependency_constraints.base_file_path == base_file_path
         assert parsed_dependency_constraints.packages == packages
+
+
+@pytest.mark.parametrize(
+    ("image", "deprecated"),
+    [
+        ("manylinux1", True),
+        ("manylinux2010", True),
+        ("manylinux2014", False),
+        ("manylinux_2_24", True),
+        ("manylinux_2_28", False),
+        ("manylinux_2_34", False),
+        ("musllinux_1_1", True),
+        ("musllinux_1_2", False),
+    ],
+)
+def test_deprecated_image(image: str, deprecated: bool, capsys: pytest.CaptureFixture[str]) -> None:
+    args = CommandLineArguments.defaults()
+    env = {
+        "CIBW_ARCHS": "x86_64",
+        "CIBW_MANYLINUX_X86_64_IMAGE": image if image.startswith("manylinux") else "",
+        "CIBW_MUSLLINUX_X86_64_IMAGE": image if image.startswith("musllinux") else "",
+    }
+    options = Options(platform="linux", command_line_arguments=args, env=env)
+    bo = options.build_options(None)
+    images = bo.manylinux_images if image.startswith("manylinux") else bo.musllinux_images
+    assert images is not None
+    resolved_image = images["x86_64"]
+    captured = capsys.readouterr()
+    if deprecated:
+        assert f"Deprecated image {image!r}" in captured.err
+        assert f"{resolved_image!r}" in captured.err
+    else:
+        assert captured.err == ""

From c087d85b69012f624e0321468b40345d921c72e5 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 12 Mar 2025 08:55:23 +0000
Subject: [PATCH 010/193] Update dependencies

---
 .../resources/constraints-pyodide312.txt      |  2 +-
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python38.txt        |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 42 +++++++++----------
 cibuildwheel/resources/virtualenv.toml        |  2 +-
 10 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 8ce1bf4fc..1ee368fca 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -102,7 +102,7 @@ unearth==0.17.2
     # via pyodide-build
 urllib3==2.3.0
     # via requests
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via
     #   build
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index a65ea62b1..c6355b3ba 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index c5892095e..72784c87f 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.20.2
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index a65ea62b1..c6355b3ba 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index ff8b1b1f7..c1ba948c1 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.12.2
     # via delocate
-virtualenv==20.29.2
+virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index a6d45df10..4de150987 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,58 +1,58 @@
 [x86_64]
 manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.09-1
 
 [i686]
 manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.09-1
 
 [pypy_x86_64]
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
 
 [pypy_i686]
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.09-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.09-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.09-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.02.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.09-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.02.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.02.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.02.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.02.28-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.09-1
 
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index cb13b37a9..b791121a1 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1,2 +1,2 @@
 py36 = { version = "20.21.1", url = "https://github.com/pypa/get-virtualenv/blob/20.21.1/public/virtualenv.pyz?raw=true" }
-default = { version = "20.29.1", url = "https://github.com/pypa/get-virtualenv/blob/20.29.1/public/virtualenv.pyz?raw=true" }
+default = { version = "20.29.3", url = "https://github.com/pypa/get-virtualenv/blob/20.29.3/public/virtualenv.pyz?raw=true" }

From a880bf5105e70f0add65d840db82a7cc6c1555e4 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 11 Mar 2025 16:44:06 -0400
Subject: [PATCH 011/193] fix: warn on deprecated images being set (#2312)

* fix: warn on deprecated images being set

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: handle repeated warning messages

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Joe Rickerby <joerick@mac.com>

* fix: try making the warnings once per Options

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 cibuildwheel/options.py | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 3fb9ebb73..9179f40b5 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -567,6 +567,7 @@ def __init__(
         self.command_line_arguments = command_line_arguments
         self.env = env
         self._defaults = defaults
+        self._image_warnings = set[str]()
 
         self.reader = OptionsReader(
             None if defaults else self.config_file_path,
@@ -785,6 +786,24 @@ def build_options(self, identifier: str | None) -> BuildOptions:
                         # default to manylinux2014
                         image = pinned_images["manylinux2014"]
                     elif config_value in pinned_images:
+                        if (
+                            config_value
+                            in {
+                                "manylinux1",
+                                "manylinux2010",
+                                "manylinux_2_24",
+                                "musllinux_1_1",
+                            }
+                            and config_value not in self._image_warnings
+                        ):
+                            self._image_warnings.add(config_value)
+                            msg = (
+                                f"Deprecated image {config_value!r}. This value will not work"
+                                " in a future version of cibuildwheel. Either upgrade to a supported"
+                                " image or continue using the deprecated image by pinning directly"
+                                f" to {pinned_images[config_value]!r}."
+                            )
+                            log.warning(msg)
                         image = pinned_images[config_value]
                     else:
                         image = config_value

From c25fe603855d4d4dbbb013375765345f346aece0 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Wed, 12 Mar 2025 09:41:51 +0100
Subject: [PATCH 012/193] fix: image deprecation warning (#2314)

---
 cibuildwheel/options.py   | 36 +++++++++++++++++-------------------
 unit_test/options_test.py | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 9179f40b5..14e1d07a6 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -567,7 +567,7 @@ def __init__(
         self.command_line_arguments = command_line_arguments
         self.env = env
         self._defaults = defaults
-        self._image_warnings = set[str]()
+        self._image_warnings: set[str] = set()
 
         self.reader = OptionsReader(
             None if defaults else self.config_file_path,
@@ -689,6 +689,20 @@ def globals(self) -> GlobalOptions:
             allow_empty=allow_empty,
         )
 
+    def _check_pinned_image(self, value: str, pinned_images: Mapping[str, str]) -> None:
+        if (
+            value in {"manylinux1", "manylinux2010", "manylinux_2_24", "musllinux_1_1"}
+            and value not in self._image_warnings
+        ):
+            self._image_warnings.add(value)
+            msg = (
+                f"Deprecated image {value!r}. This value will not work"
+                " in a future version of cibuildwheel. Either upgrade to a supported"
+                " image or continue using the deprecated image by pinning directly"
+                f" to {pinned_images[value]!r}."
+            )
+            log.warning(msg)
+
     def build_options(self, identifier: str | None) -> BuildOptions:
         """
         Compute BuildOptions for a single run configuration.
@@ -786,24 +800,7 @@ def build_options(self, identifier: str | None) -> BuildOptions:
                         # default to manylinux2014
                         image = pinned_images["manylinux2014"]
                     elif config_value in pinned_images:
-                        if (
-                            config_value
-                            in {
-                                "manylinux1",
-                                "manylinux2010",
-                                "manylinux_2_24",
-                                "musllinux_1_1",
-                            }
-                            and config_value not in self._image_warnings
-                        ):
-                            self._image_warnings.add(config_value)
-                            msg = (
-                                f"Deprecated image {config_value!r}. This value will not work"
-                                " in a future version of cibuildwheel. Either upgrade to a supported"
-                                " image or continue using the deprecated image by pinning directly"
-                                f" to {pinned_images[config_value]!r}."
-                            )
-                            log.warning(msg)
+                        self._check_pinned_image(config_value, pinned_images)
                         image = pinned_images[config_value]
                     else:
                         image = config_value
@@ -818,6 +815,7 @@ def build_options(self, identifier: str | None) -> BuildOptions:
                     if not config_value:
                         image = pinned_images["musllinux_1_2"]
                     elif config_value in pinned_images:
+                        self._check_pinned_image(config_value, pinned_images)
                         image = pinned_images[config_value]
                     else:
                         image = config_value
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index fb6727b0c..a38393f8a 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -459,3 +459,36 @@ def test_free_threaded_support(
         assert EnableGroups.CPythonFreeThreading in options.globals.build_selector.enable
     else:
         assert EnableGroups.CPythonFreeThreading not in options.globals.build_selector.enable
+
+
+@pytest.mark.parametrize(
+    ("image", "deprecated"),
+    [
+        ("manylinux1", True),
+        ("manylinux2010", True),
+        ("manylinux2014", False),
+        ("manylinux_2_24", True),
+        ("manylinux_2_28", False),
+        ("manylinux_2_34", False),
+        ("musllinux_1_1", True),
+        ("musllinux_1_2", False),
+    ],
+)
+def test_deprecated_image(image: str, deprecated: bool, capsys: pytest.CaptureFixture[str]) -> None:
+    args = CommandLineArguments.defaults()
+    env = {
+        "CIBW_ARCHS": "x86_64",
+        "CIBW_MANYLINUX_X86_64_IMAGE": image if image.startswith("manylinux") else "",
+        "CIBW_MUSLLINUX_X86_64_IMAGE": image if image.startswith("musllinux") else "",
+    }
+    options = Options(platform="linux", command_line_arguments=args, env=env)
+    bo = options.build_options(None)
+    images = bo.manylinux_images if image.startswith("manylinux") else bo.musllinux_images
+    assert images is not None
+    resolved_image = images["x86_64"]
+    captured = capsys.readouterr()
+    if deprecated:
+        assert f"Deprecated image {image!r}" in captured.err
+        assert f"{resolved_image!r}" in captured.err
+    else:
+        assert captured.err == ""

From 6e1527b153c481c51f987a5e3a1bed216b16a260 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 12 Mar 2025 09:15:58 +0000
Subject: [PATCH 013/193] Fix unit test when other warnings are present

---
 unit_test/options_test.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index a38393f8a..0e1c38a4b 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -491,4 +491,4 @@ def test_deprecated_image(image: str, deprecated: bool, capsys: pytest.CaptureFi
         assert f"Deprecated image {image!r}" in captured.err
         assert f"{resolved_image!r}" in captured.err
     else:
-        assert captured.err == ""
+        assert "Deprecated image" not in captured.err

From 42728e866bbc80d544a70825bd9990b9a26f1a50 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sat, 15 Mar 2025 17:58:14 +0000
Subject: [PATCH 014/193] Bump version: v2.23.1

---
 README.md                              | 18 ++++++++----------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  7 +++++++
 docs/faq.md                            |  6 +++---
 docs/setup.md                          |  4 ++--
 examples/appveyor-minimal.yml          |  2 +-
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 19 files changed, 42 insertions(+), 37 deletions(-)

diff --git a/README.md b/README.md
index 714ef05ca..cd63c51ce 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==2.23.0
+        run: python -m pip install cibuildwheel==2.23.1
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -215,6 +215,13 @@ Changelog
 
 <!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
 
+### v2.23.1
+
+_15 March 2025_
+
+- ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
+- 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
+
 ### v2.23.0
 
 _1 March 2025_
@@ -261,15 +268,6 @@ _2 October 2024_
 
 Note: the default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) is **scheduled to change** from `manylinux2014` to `manylinux_2_28` in a cibuildwheel release on or after **6th May 2025** - you can set the value now to avoid getting upgraded if you want. (#1992)
 
-### v2.21.1
-
-_16 September 2024_
-
-- 🐛 Fix a bug in the Linux build, where files copied to the container would have invalid ownership permissions (#2007)
-- 🐛 Fix a bug on Windows where cibuildwheel would call upon `uv` to install dependencies for versions of CPython that it does not support (#2005)
-- 🐛 Fix a bug where `uv 0.4.10` would not use the right Python when testing on Linux. (#2008)
-- 🛠 Bump our documentation pins, fixes an issue with a missing package (#2011)
-
 <!-- END bin/update_readme_changelog.py -->
 
 ---
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 91d675f07..542a94157 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1,3 +1,3 @@
 from __future__ import annotations
 
-__version__ = "2.23.0"
+__version__ = "2.23.1"
diff --git a/docs/changelog.md b/docs/changelog.md
index 7b60b73ff..87f40492d 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,13 @@ title: Changelog
 
 # Changelog
 
+### v2.23.1
+
+_15 March 2025_
+
+- ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
+- 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
+
 ### v2.23.0
 
 _1 March 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 7ef36636d..8df6278e9 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -142,7 +142,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v2.23.0
+uses: pypa/cibuildwheel@v2.23.1
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -164,7 +164,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==2.23.0
+cibuildwheel==2.23.1
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -328,7 +328,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==2.23.0 --output-dir wheelhouse
+pipx run cibuildwheel==2.23.1 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/docs/setup.md b/docs/setup.md
index 2ddea4342..93c3d95ed 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -161,7 +161,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
           - uses: actions/checkout@v4
 
           - name: Build wheels
-            run: pipx run cibuildwheel==2.23.0
+            run: pipx run cibuildwheel==2.23.1
 
           - uses: actions/upload-artifact@v4
             with:
@@ -198,7 +198,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
           - uses: actions/setup-python@v5
 
           - name: Install cibuildwheel
-            run: python -m pip install cibuildwheel==2.23.0
+            run: python -m pip install cibuildwheel==2.23.1
 
           - name: Build wheels
             run: python -m cibuildwheel --output-dir wheelhouse
diff --git a/examples/appveyor-minimal.yml b/examples/appveyor-minimal.yml
index cf013cd8e..e1c9d39b6 100644
--- a/examples/appveyor-minimal.yml
+++ b/examples/appveyor-minimal.yml
@@ -9,7 +9,7 @@ environment:
 
 stack: python 3.12
 
-install: python -m pip install cibuildwheel==2.23.0
+install: python -m pip install cibuildwheel==2.23.1
 
 build_script: python -m cibuildwheel --output-dir wheelhouse
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 4b5d3e14e..1aed664d8 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==2.23.0
+        pip3 install cibuildwheel==2.23.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==2.23.0
+        python3 -m pip install cibuildwheel==2.23.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==2.23.0
+        pip install cibuildwheel==2.23.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 5fec2b5f9..f0bfec4f4 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.0
+            python3 -m pip install --user cibuildwheel==2.23.1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.0
+            python3 -m pip install --user cibuildwheel==2.23.1
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==2.23.0
+            pip3 install cibuildwheel==2.23.1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index a48e15010..c8f66a189 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.0
+    - python -m pip install cibuildwheel==2.23.1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index c09baa335..7bfb28cad 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.0
+    - python -m pip install cibuildwheel==2.23.1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 2061be4d2..c2060150f 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -23,7 +23,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.0
+        uses: pypa/cibuildwheel@v2.23.1
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 91c2a7edf..fb1a52626 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.0
+        uses: pypa/cibuildwheel@v2.23.1
         # env:
         #   CIBW_SOME_OPTION: value
         #    ...
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index c3da30a4a..d79145715 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.0
+        uses: pypa/cibuildwheel@v2.23.1
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 10154026c..7018a9c1f 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==2.23.0
+    - python -m pip install cibuildwheel==2.23.1
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==2.23.0
+    - py -m pip install cibuildwheel==2.23.1
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==2.23.0
+    - python3 -m pip install cibuildwheel==2.23.1
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index dab068fe3..0e7484167 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==2.23.0
+    - python -m pip install cibuildwheel==2.23.1
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index f306def39..88e1923ae 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.0
+  - python3 -m pip install cibuildwheel==2.23.1
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 431c9421b..0db795b14 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.0
+  - python3 -m pip install cibuildwheel==2.23.1
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 761558173..6922a50bf 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==2.23.0 twine
+      install: python3 -m pip install cibuildwheel==2.23.1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==2.23.0 twine
+      install: python3 -m pip install cibuildwheel==2.23.1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 9e0dbb5c9..25ad14548 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "2.23.0"
+version = "2.23.1"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 1826f7068262b8cb6b59fe9dd1b408a825d63fa6 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 17 Mar 2025 10:10:58 -0400
Subject: [PATCH 015/193] [Bot] Update dependencies (#2319)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |  6 +--
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 cibuildwheel/resources/nodejs.toml            |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 docs/working-examples.md                      |  8 +--
 10 files changed, 41 insertions(+), 41 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 1ee368fca..59f7c98e9 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -2,7 +2,7 @@
 #    nox -s update_constraints
 annotated-types==0.7.0
     # via pydantic
-anyio==4.8.0
+anyio==4.9.0
     # via httpx
 auditwheel-emscripten==0.0.16
     # via pyodide-build
@@ -23,7 +23,7 @@ cmake==3.31.6
     # via pyodide-build
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 h11==0.14.0
     # via httpcore
@@ -98,7 +98,7 @@ typing-extensions==4.12.2
     #   pydantic
     #   pydantic-core
     #   typer
-unearth==0.17.2
+unearth==0.17.3
     # via pyodide-build
 urllib3==2.3.0
     # via requests
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index c6355b3ba..e3b1e97a2 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 importlib-metadata==8.6.1
     # via build
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index c1ba948c1..f6dbd9b8d 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index c1ba948c1..f6dbd9b8d 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index c1ba948c1..f6dbd9b8d 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index c6355b3ba..e3b1e97a2 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 importlib-metadata==8.6.1
     # via build
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index c1ba948c1..f6dbd9b8d 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
diff --git a/cibuildwheel/resources/nodejs.toml b/cibuildwheel/resources/nodejs.toml
index 91a71f773..ad93f2d7b 100644
--- a/cibuildwheel/resources/nodejs.toml
+++ b/cibuildwheel/resources/nodejs.toml
@@ -1,3 +1,3 @@
 url = "https://nodejs.org/dist/"
 v22 = "v22.14.0"
-v20 = "v20.18.3"
+v20 = "v20.19.0"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 2049734c5..a4e2e2964 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,64 +1,64 @@
 [x86_64]
 manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.15-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.15-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.15-1
 
 [i686]
 manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.15-1
 
 [pypy_x86_64]
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.15-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.15-1
 
 [pypy_i686]
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.15-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.15-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.15-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.15-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.15-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.15-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.15-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.15-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.15-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.15-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.15-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.15-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.09-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.09-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.15-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.15-1
 
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 1d25b4aa5..e960100bc 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -29,8 +29,8 @@ title: Working examples
 | [asyncpg][]                       | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A fast PostgreSQL Database Client Library for Python/asyncio. |
 | [cmake][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
 | [scikit-image][]                  | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Image processing library. Uses cibuildwheel to build and test a project that uses Cython with platform-native code.  |
-| [twisted-iocpsupport][]           | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
 | [PyOxidizer][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool |
+| [twisted-iocpsupport][]           | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
 | [cvxpy][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. |
 | [pedalboard][]                    | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | A Python library for working with audio data and audio plugins by wrapping the [JUCE](https://github.com/juce-framework/JUCE/) C++ framework. Uses cibuildwheel to deploy on as many operating systems and Python versions as possible with only one dependency (any NumPy).  |
 | [websockets][]                    | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module.  |
@@ -94,8 +94,8 @@ title: Working examples
 | [streaming-form-data][]           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython |
 | [power-grid-model][]              | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis |
 | [bx-python][]                     | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
-| [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
 | [pybase64][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
+| [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
 | [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
 | [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
@@ -139,8 +139,8 @@ title: Working examples
 [asyncpg]: https://github.com/MagicStack/asyncpg
 [cmake]: https://github.com/scikit-build/cmake-python-distributions
 [scikit-image]: https://github.com/scikit-image/scikit-image
-[twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
 [PyOxidizer]: https://github.com/indygreg/PyOxidizer
+[twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
 [cvxpy]: https://github.com/cvxpy/cvxpy
 [pedalboard]: https://github.com/spotify/pedalboard
 [websockets]: https://github.com/python-websockets/websockets
@@ -204,8 +204,8 @@ title: Working examples
 [streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data
 [power-grid-model]: https://github.com/PowerGridModel/power-grid-model
 [bx-python]: https://github.com/bxlab/bx-python
-[boost-histogram]: https://github.com/scikit-hep/boost-histogram
 [pybase64]: https://github.com/mayeut/pybase64
+[boost-histogram]: https://github.com/scikit-hep/boost-histogram
 [Python-WebRTC]: https://github.com/MarshalX/python-webrtc
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
 [fathon]: https://github.com/stfbnc/fathon

From 21888b531f43600b54356a7dda5742ffdacd027e Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 21 Mar 2025 12:51:51 +0000
Subject: [PATCH 016/193] [pre-commit.ci] pre-commit autoupdate (#2320)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0)

* chore: use Path.iterdir

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: Path.iterdir usage

os.listdir returned entry names rather than their full path

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
---
 .pre-commit-config.yaml  | 2 +-
 test/test_environment.py | 2 +-
 test/test_from_sdist.py  | 2 +-
 test/test_testing.py     | 3 +--
 test/utils.py            | 2 +-
 5 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index dd425c894..870f73522 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.9.10
+  rev: v0.11.0
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
diff --git a/test/test_environment.py b/test/test_environment.py
index 4f8a3170f..6e7ed6fd8 100644
--- a/test/test_environment.py
+++ b/test/test_environment.py
@@ -89,7 +89,7 @@ def test_overridden_path(tmp_path, capfd):
                 },
             )
 
-    assert len(os.listdir(output_dir)) == 0
+    assert len(list(output_dir.iterdir())) == 0
     captured = capfd.readouterr()
     assert "python available on PATH doesn't match our installed instance" in captured.err.replace(
         "venv", "installed"
diff --git a/test/test_from_sdist.py b/test/test_from_sdist.py
index 3bb7926db..b916271cc 100644
--- a/test/test_from_sdist.py
+++ b/test/test_from_sdist.py
@@ -53,7 +53,7 @@ def cibuildwheel_from_sdist_run(
             env=env,
             check=True,
         )
-        return os.listdir(tmp_output_dir)
+        return [p.name for p in Path(tmp_output_dir).iterdir()]
 
 
 # tests
diff --git a/test/test_testing.py b/test/test_testing.py
index 4dfda9141..8bd810f50 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -1,5 +1,4 @@
 import inspect
-import os
 import subprocess
 import textwrap
 from pathlib import Path
@@ -180,7 +179,7 @@ def test_failing_test(tmp_path):
             },
         )
 
-    assert len(os.listdir(output_dir)) == 0
+    assert len(list(output_dir.iterdir())) == 0
 
 
 @pytest.mark.parametrize("test_runner", ["pytest", "unittest"])
diff --git a/test/utils.py b/test/utils.py
index 921494e26..4548f0a52 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -140,7 +140,7 @@ def cibuildwheel_run(
             cwd=project_path,
             check=True,
         )
-        wheels = os.listdir(output_dir or tmp_output_dir)
+        wheels = [p.name for p in (output_dir or Path(tmp_output_dir)).iterdir()]
     return wheels
 
 

From be2a707315f64624d12d637e5f7df4418471f61a Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Fri, 21 Mar 2025 09:49:04 -0400
Subject: [PATCH 017/193] feat: use build by default (#2321)

* feat: use build by default

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: test/test_before_build.py:test

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
---
 cibuildwheel/ios.py       | 2 +-
 cibuildwheel/linux.py     | 2 +-
 cibuildwheel/macos.py     | 2 +-
 cibuildwheel/windows.py   | 2 +-
 docs/options.md           | 6 +++---
 test/test_before_build.py | 5 +++++
 6 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/cibuildwheel/ios.py b/cibuildwheel/ios.py
index e3b6c0a47..52411e31a 100644
--- a/cibuildwheel/ios.py
+++ b/cibuildwheel/ios.py
@@ -389,7 +389,7 @@ def build(options: Options, tmp_path: Path) -> None:
 
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
-            build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
+            build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
             # uv doesn't support iOS
             if build_frontend.name == "build[uv]":
                 msg = "uv doesn't support iOS"
diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py
index 3a652b366..650577c9d 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/linux.py
@@ -202,7 +202,7 @@ def build_in_container(
         log.build_start(config.identifier)
         local_identifier_tmp_dir = local_tmp_dir / config.identifier
         build_options = options.build_options(config.identifier)
-        build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
+        build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
         use_uv = build_frontend.name == "build[uv]"
         pip = ["uv", "pip"] if use_uv else ["pip"]
 
diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py
index a6829eca9..bd05f2a99 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/macos.py
@@ -404,7 +404,7 @@ def build(options: Options, tmp_path: Path) -> None:
 
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
-            build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
+            build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
             use_uv = build_frontend.name == "build[uv]"
             uv_path = find_uv()
             if use_uv and uv_path is None:
diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py
index 268e73de6..bd9a3224c 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/windows.py
@@ -355,7 +355,7 @@ def build(options: Options, tmp_path: Path) -> None:
 
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
-            build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
+            build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
             use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
             log.build_start(config.identifier)
 
diff --git a/docs/options.md b/docs/options.md
index 080c66bb8..dd90e9fcb 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -649,7 +649,7 @@ This option can also be set using the [command-line option](#command-line)
 ## Build customization
 
 ### `CIBW_BUILD_FRONTEND` {: #build-frontend}
-> Set the tool to use to build, either "pip" (default for now), "build", or "build\[uv\]"
+> Set the tool to use to build, either "build" (default), "build/[uv/]", or "pip"
 
 Options:
 
@@ -658,8 +658,8 @@ Options:
 
 Default: `pip`
 
-Choose which build frontend to use. Can either be "pip", which will run
-`python -m pip wheel`, or "build", which will run `python -m build --wheel`.
+Choose which build frontend to use. Can either be "build", which will run
+`python -m build --wheel`, or "pip", which will run `python -m pip wheel`.
 
 You can also use "build\[uv\]", which will use an external [uv][] everywhere
 possible, both through `--installer=uv` passed to build, as well as when making
diff --git a/test/test_before_build.py b/test/test_before_build.py
index c817c87ca..19a770133 100644
--- a/test/test_before_build.py
+++ b/test/test_before_build.py
@@ -44,6 +44,10 @@ def test(tmp_path):
         """python -c "import sys; open('{project}/pythonversion_bb.txt', 'w').write(sys.version)" && """
         f'''python -c "import sys; open('{{project}}/pythonprefix_bb.txt', 'w').write({SYS_PREFIX})"'''
     )
+    frontend = "build"
+    if utils.platform != "pyodide":
+        before_build = f"python -m pip install setuptools && {before_build}"
+        frontend = f"{frontend};args: --no-isolation"
 
     # build the wheels
     actual_wheels = utils.cibuildwheel_run(
@@ -52,6 +56,7 @@ def test(tmp_path):
             # write python version information to a temporary file, this is
             # checked in setup.py
             "CIBW_BEFORE_BUILD": before_build,
+            "CIBW_BUILD_FRONTEND": frontend,
         },
     )
 

From fccd32f9b20ed2a68ceb2ddaac14b99e3b6a3b5d Mon Sep 17 00:00:00 2001
From: Hadi Alqattan <alqattanhadizaki@gmail.com>
Date: Sat, 22 Mar 2025 09:29:45 +0300
Subject: [PATCH 018/193] Doc: add a note about Rustc's minimum
 MACOSX_DEPLOYMENT_TARGET requirement (#2326)

* doc: add a note about rustc's minimum MACOSX_DEPLOYMENT_TARGET requirement

* Update docs/platforms/macos.md

* Update faq.md

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
---
 docs/faq.md             | 11 ++++++++++-
 docs/platforms/macos.md |  6 ++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/docs/faq.md b/docs/faq.md
index 8df6278e9..8e61dba21 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -294,7 +294,16 @@ CIBW_BEFORE_ALL_WINDOWS: rustup target add i686-pc-windows-msvc
 CIBW_ENVIRONMENT_LINUX: "PATH=$HOME/.cargo/bin:$PATH"
 ```
 
-Rust does not provide Cargo for musllinux 32-bit, so that needs to be skipped:
+Rust's minimum macOS target is 10.12, while CPython supports 10.9 before
+Python 3.12, so you'll need to raise the minimum:
+
+```toml
+[tool.cibuildwheel.macos.environment]
+MACOSX_DEPLOYMENT_TARGET = "10.12"
+```
+
+And Rust does not provide Cargo for musllinux 32-bit, so that needs to be
+skipped:
 
 ```toml
 [tool.cibuildwheel]
diff --git a/docs/platforms/macos.md b/docs/platforms/macos.md
index 01e8d0ec8..bf07e09c0 100644
--- a/docs/platforms/macos.md
+++ b/docs/platforms/macos.md
@@ -41,6 +41,12 @@ macOS builds will honor the `MACOSX_DEPLOYMENT_TARGET` environment variable to c
 
 If you set the value lower, cibuildwheel will cap it to the lowest supported value for each target as needed.
 
+!!! note
+    For Rust-based extensions, `Rustc` requires `MACOSX_DEPLOYMENT_TARGET` to be at
+    least 10.12. However, `cibuildwheel` defaults to 10.9 for
+    **Intel / CPython 3.8-3.11** builds. Users must manually set
+    `MACOSX_DEPLOYMENT_TARGET` to 10.12 or higher when building Rust extensions.
+
 ## Universal builds
 
 By default, macOS builds will build a single architecture wheel, using the build machine's architecture. If you need to support both x86_64 and Apple Silicon, you can use the `CIBW_ARCHS` environment variable to specify the architectures you want to build, or the value `universal2` to build a multi-architecture wheel. cibuildwheel will test x86_64 wheels (or the x86_64 slice of a `universal2` wheel) when running on Apple Silicon hardware, but it is *not* possible to test Apple Silicon wheels on x86_64 hardware.

From 462c99589a225e8c44a682e210bdd74b90be512c Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sat, 22 Mar 2025 02:32:25 -0400
Subject: [PATCH 019/193] fix: always resolve --python argument (#2328)

fix: always resolve the --python argument

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/venv.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/cibuildwheel/venv.py b/cibuildwheel/venv.py
index 53c034463..24f2fe5c9 100644
--- a/cibuildwheel/venv.py
+++ b/cibuildwheel/venv.py
@@ -96,6 +96,10 @@ def virtualenv(
     dependency_constraint_flags are ignored since nothing is installed in the
     venv. Otherwise, pip is installed, and setuptools + wheel if Python < 3.12.
     """
+
+    # virtualenv may fail if this is a symlink.
+    python = python.resolve()
+
     assert python.exists()
 
     if use_uv:

From 3b9c8d4d22728ef745bb32236209d03d0c71daf6 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Sat, 22 Mar 2025 17:25:58 +0100
Subject: [PATCH 020/193] fix: don't seed setuptools/wheel in tests virtual
 environments. (#2329)

---
 cibuildwheel/linux.py                         |  7 +---
 cibuildwheel/macos.py                         | 10 +++--
 .../resources/constraints-python37.txt        | 39 -------------------
 cibuildwheel/venv.py                          | 33 +++++-----------
 cibuildwheel/windows.py                       | 11 +++---
 5 files changed, 22 insertions(+), 78 deletions(-)
 delete mode 100644 cibuildwheel/resources/constraints-python37.txt

diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py
index 650577c9d..7f0a6e77d 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/linux.py
@@ -8,8 +8,6 @@
 from pathlib import Path, PurePath, PurePosixPath
 from typing import assert_never
 
-from packaging.version import Version
-
 from . import errors
 from .architecture import Architecture
 from .frontend import BuildFrontendConfig, get_build_frontend_extra_flags
@@ -351,10 +349,7 @@ def build_in_container(
                 container.call(["uv", "venv", venv_dir, "--python", python_bin / "python"], env=env)
             else:
                 # Use embedded dependencies from virtualenv to ensure determinism
-                venv_args = ["--no-periodic-update", "--pip=embed"]
-                # In Python<3.12, setuptools & wheel are installed as well
-                if Version(config.version) < Version("3.12"):
-                    venv_args.extend(("--setuptools=embed", "--wheel=embed"))
+                venv_args = ["--no-periodic-update", "--pip=embed", "--no-setuptools", "--no-wheel"]
                 container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)
 
             virtualenv_env = env.copy()
diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py
index bd05f2a99..b27348d7a 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/macos.py
@@ -643,10 +643,12 @@ def build(options: Options, tmp_path: Path) -> None:
                     else:
                         pip_install = functools.partial(call_with_arch, *pip, "install")
                         # Use pip version from the initial env to ensure determinism
-                        venv_args = ["--no-periodic-update", f"--pip={pip_version}"]
-                        # In Python<3.12, setuptools & wheel are installed as well, use virtualenv embedded ones
-                        if Version(config.version) < Version("3.12"):
-                            venv_args.extend(("--setuptools=embed", "--wheel=embed"))
+                        venv_args = [
+                            "--no-periodic-update",
+                            f"--pip={pip_version}",
+                            "--no-setuptools",
+                            "--no-wheel",
+                        ]
                         call_with_arch("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
 
                     virtualenv_env = env.copy()
diff --git a/cibuildwheel/resources/constraints-python37.txt b/cibuildwheel/resources/constraints-python37.txt
deleted file mode 100644
index 55e33eec5..000000000
--- a/cibuildwheel/resources/constraints-python37.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-# This file was autogenerated by uv via the following command:
-#    nox -s update_constraints
-altgraph==0.17.4
-    # via macholib
-build==1.1.1
-    # via -r cibuildwheel/resources/constraints.in
-delocate==0.12.0
-    # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
-    # via virtualenv
-filelock==3.12.2
-    # via virtualenv
-importlib-metadata==6.7.0
-    # via
-    #   build
-    #   virtualenv
-macholib==1.16.3
-    # via delocate
-packaging==24.0
-    # via
-    #   build
-    #   delocate
-pip==24.0
-    # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.0.0
-    # via virtualenv
-pyproject-hooks==1.2.0
-    # via build
-tomli==2.0.1
-    # via build
-typing-extensions==4.7.1
-    # via
-    #   delocate
-    #   importlib-metadata
-    #   platformdirs
-virtualenv==20.26.6
-    # via -r cibuildwheel/resources/constraints.in
-zipp==3.15.0
-    # via importlib-metadata
diff --git a/cibuildwheel/venv.py b/cibuildwheel/venv.py
index 24f2fe5c9..6f7ca400a 100644
--- a/cibuildwheel/venv.py
+++ b/cibuildwheel/venv.py
@@ -36,10 +36,9 @@ def _ensure_virtualenv(version: str) -> Path:
     return path
 
 
-def _parse_constraints_for_virtualenv(
-    seed_packages: list[str],
+def _parse_pip_constraint_for_virtualenv(
     dependency_constraint_flags: Sequence[PathOrStr],
-) -> dict[str, str]:
+) -> str:
     """
     Parses the constraints file referenced by `dependency_constraint_flags` and returns a dict where
     the key is the package name, and the value is the constraint version.
@@ -50,8 +49,6 @@ def _parse_constraints_for_virtualenv(
     {macos|windows}.setup_python function.
     """
     assert len(dependency_constraint_flags) in {0, 2}
-    # only seed pip if other seed packages do not appear in a constraint file
-    constraints_dict = {"pip": "embed"}
     if len(dependency_constraint_flags) == 2:
         assert dependency_constraint_flags[0] == "-c"
         constraint_path = Path(dependency_constraint_flags[1])
@@ -67,7 +64,7 @@ def _parse_constraints_for_virtualenv(
                     requirement = Requirement(line)
                     package = requirement.name
                     if (
-                        package not in seed_packages
+                        package != "pip"
                         or requirement.url is not None
                         or requirement.marker is not None
                         or len(requirement.extras) != 0
@@ -77,10 +74,10 @@ def _parse_constraints_for_virtualenv(
                     specifier = next(iter(requirement.specifier))
                     if specifier.operator != "==":
                         continue
-                    constraints_dict[package] = specifier.version
+                    return specifier.version
                 except InvalidRequirement:
                     continue
-    return constraints_dict
+    return "embed"
 
 
 def virtualenv(
@@ -94,7 +91,7 @@ def virtualenv(
     """
     Create a virtual environment. If `use_uv` is True,
     dependency_constraint_flags are ignored since nothing is installed in the
-    venv. Otherwise, pip is installed, and setuptools + wheel if Python < 3.12.
+    venv. Otherwise, pip is installed.
     """
 
     # virtualenv may fail if this is a symlink.
@@ -106,16 +103,8 @@ def virtualenv(
         call("uv", "venv", venv_path, "--python", python)
     else:
         virtualenv_app = _ensure_virtualenv(version)
-        allowed_seed_packages = ["pip", "setuptools", "wheel"]
-        constraints = _parse_constraints_for_virtualenv(
-            allowed_seed_packages, dependency_constraint_flags
-        )
-        additional_flags: list[str] = []
-        for package in allowed_seed_packages:
-            if package in constraints:
-                additional_flags.append(f"--{package}={constraints[package]}")
-            else:
-                additional_flags.append(f"--no-{package}")
+        pip_constraint = _parse_pip_constraint_for_virtualenv(dependency_constraint_flags)
+        additional_flags = [f"--pip={pip_constraint}", "--no-setuptools", "--no-wheel"]
 
         # Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
         # environment. The initial cost is a bit higher but reusing is much faster.
@@ -123,11 +112,7 @@ def virtualenv(
         # Requires pip>=19.3 so disabling for "embed" because this means we don't know what's the
         # version of pip that will end-up installed.
         # c.f. https://virtualenv.pypa.io/en/latest/cli_interface.html#section-seeder
-        if (
-            not _IS_WIN
-            and constraints["pip"] != "embed"
-            and Version(constraints["pip"]) >= Version("19.3")
-        ):
+        if not _IS_WIN and pip_constraint != "embed" and Version(pip_constraint) >= Version("19.3"):
             additional_flags.append("--symlink-app-data")
 
         call(
diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py
index bd9a3224c..374b68d71 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/windows.py
@@ -10,7 +10,6 @@
 from typing import assert_never
 
 from filelock import FileLock
-from packaging.version import Version
 
 from . import errors
 from .architecture import Architecture
@@ -497,10 +496,12 @@ def build(options: Options, tmp_path: Path) -> None:
                     call("uv", "venv", venv_dir, f"--python={base_python}", env=env)
                 else:
                     # Use pip version from the initial env to ensure determinism
-                    venv_args = ["--no-periodic-update", f"--pip={pip_version}"]
-                    # In Python<3.12, setuptools & wheel are installed as well, use virtualenv embedded ones
-                    if Version(config.version) < Version("3.12"):
-                        venv_args.extend(("--setuptools=embed", "--wheel=embed"))
+                    venv_args = [
+                        "--no-periodic-update",
+                        f"--pip={pip_version}",
+                        "--no-setuptools",
+                        "--no-wheel",
+                    ]
                     call("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
 
                 virtualenv_env = env.copy()

From 059d8ea25a5bced1fc2e5f7b0cc4b8df4a00730c Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Sat, 22 Mar 2025 17:26:38 +0100
Subject: [PATCH 021/193] feat: drop EOL manylinux images (#2316)

* drop EOL manylinux images

* chore: use multi-arch manylinux images as a source for image tag

Using multi-arch images directly messes with parallel tests for now.
Using them as a source for image tag ensures that all architecture specific tags are pointing to the same tag for a given manylinux / musllinux policy.

* Apply review suggestion

Co-authored-by: Joe Rickerby <joerick@mac.com>

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 bin/update_docker.py                          | 94 +++++++------------
 cibuildwheel/options.py                       | 26 ++---
 .../resources/pinned_docker_images.cfg        | 35 ++-----
 docs/cpp_standards.md                         |  7 +-
 docs/faq.md                                   |  2 +-
 docs/options.md                               | 60 +++++-------
 test/test_container_images.py                 |  2 +-
 test/test_linux_python.py                     |  9 +-
 test/test_manylinuxXXXX_only.py               | 47 +---------
 test/test_musllinux_X_Y_only.py               |  6 +-
 test/test_testing.py                          |  4 -
 unit_test/main_tests/main_options_test.py     |  9 --
 unit_test/oci_container_test.py               | 16 +---
 unit_test/option_prepare_test.py              | 21 +----
 unit_test/options_test.py                     | 38 +++++---
 unit_test/options_toml_test.py                |  4 +-
 16 files changed, 128 insertions(+), 252 deletions(-)

diff --git a/bin/update_docker.py b/bin/update_docker.py
index 11c3de18d..3f23d75da 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -14,74 +14,45 @@
 @dataclass(frozen=True)
 class Image:
     manylinux_version: str
-    platform: str
+    platforms: list[str]
     image_name: str
-    tag: str | None  # Set this to pin the image
+    tag: str | None = None  # Set this to pin the image
+    use_platform_suffix: bool = False
 
 
 class PyPAImage(Image):
-    def __init__(self, manylinux_version: str, platform: str, tag: str | None):
-        platform_no_pypy = platform.removeprefix("pypy_")
-        image_name = f"quay.io/pypa/{manylinux_version}_{platform_no_pypy}"
-        super().__init__(manylinux_version, platform, image_name, tag)
+    def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None = None):
+        image_name = f"quay.io/pypa/{manylinux_version}"
+        super().__init__(manylinux_version, platforms, image_name, tag, True)
 
 
 images = [
-    # manylinux1 images, EOL -> use tag
-    PyPAImage("manylinux1", "x86_64", "2024-04-29-76807b8"),
-    PyPAImage("manylinux1", "i686", "2024-04-29-76807b8"),
-    # manylinux2010 images, EOL -> use tag
-    PyPAImage("manylinux2010", "x86_64", "2022-08-05-4535177"),
-    PyPAImage("manylinux2010", "i686", "2022-08-05-4535177"),
-    PyPAImage("manylinux2010", "pypy_x86_64", "2022-08-05-4535177"),
-    PyPAImage("manylinux2010", "pypy_i686", "2022-08-05-4535177"),
     # manylinux2014 images
-    PyPAImage("manylinux2014", "x86_64", None),
-    PyPAImage("manylinux2014", "i686", None),
-    PyPAImage("manylinux2014", "aarch64", None),
-    PyPAImage("manylinux2014", "ppc64le", None),
-    PyPAImage("manylinux2014", "s390x", None),
-    PyPAImage("manylinux2014", "pypy_x86_64", None),
-    PyPAImage("manylinux2014", "pypy_i686", None),
-    PyPAImage("manylinux2014", "pypy_aarch64", None),
-    # manylinux_2_24 images, EOL -> use tag
-    PyPAImage("manylinux_2_24", "x86_64", "2022-12-26-0d38463"),
-    PyPAImage("manylinux_2_24", "i686", "2022-12-26-0d38463"),
-    PyPAImage("manylinux_2_24", "aarch64", "2022-12-26-0d38463"),
-    PyPAImage("manylinux_2_24", "ppc64le", "2022-12-26-0d38463"),
-    PyPAImage("manylinux_2_24", "s390x", "2022-12-26-0d38463"),
-    PyPAImage("manylinux_2_24", "pypy_x86_64", "2022-12-26-0d38463"),
-    PyPAImage("manylinux_2_24", "pypy_i686", "2022-12-26-0d38463"),
-    PyPAImage("manylinux_2_24", "pypy_aarch64", "2022-12-26-0d38463"),
+    PyPAImage(
+        "manylinux2014",
+        [
+            "x86_64",
+            "i686",
+            "aarch64",
+            "ppc64le",
+            "s390x",
+            "pypy_x86_64",
+            "pypy_i686",
+            "pypy_aarch64",
+        ],
+    ),
     # manylinux_2_28 images
-    PyPAImage("manylinux_2_28", "x86_64", None),
-    PyPAImage("manylinux_2_28", "aarch64", None),
-    PyPAImage("manylinux_2_28", "ppc64le", None),
-    PyPAImage("manylinux_2_28", "s390x", None),
-    PyPAImage("manylinux_2_28", "pypy_x86_64", None),
-    PyPAImage("manylinux_2_28", "pypy_aarch64", None),
+    PyPAImage(
+        "manylinux_2_28", ["x86_64", "aarch64", "ppc64le", "s390x", "pypy_x86_64", "pypy_aarch64"]
+    ),
     # manylinux_2_31 images
-    PyPAImage("manylinux_2_31", "armv7l", None),
+    PyPAImage("manylinux_2_31", ["armv7l"]),
     # manylinux_2_34 images
-    PyPAImage("manylinux_2_34", "x86_64", None),
-    PyPAImage("manylinux_2_34", "aarch64", None),
-    PyPAImage("manylinux_2_34", "ppc64le", None),
-    PyPAImage("manylinux_2_34", "s390x", None),
-    PyPAImage("manylinux_2_34", "pypy_x86_64", None),
-    PyPAImage("manylinux_2_34", "pypy_aarch64", None),
-    # musllinux_1_1 images, EOL -> use tag
-    PyPAImage("musllinux_1_1", "x86_64", "2024.10.26-1"),
-    PyPAImage("musllinux_1_1", "i686", "2024.10.26-1"),
-    PyPAImage("musllinux_1_1", "aarch64", "2024.10.26-1"),
-    PyPAImage("musllinux_1_1", "ppc64le", "2024.10.26-1"),
-    PyPAImage("musllinux_1_1", "s390x", "2024.10.26-1"),
+    PyPAImage(
+        "manylinux_2_34", ["x86_64", "aarch64", "ppc64le", "s390x", "pypy_x86_64", "pypy_aarch64"]
+    ),
     # musllinux_1_2 images
-    PyPAImage("musllinux_1_2", "x86_64", None),
-    PyPAImage("musllinux_1_2", "i686", None),
-    PyPAImage("musllinux_1_2", "aarch64", None),
-    PyPAImage("musllinux_1_2", "ppc64le", None),
-    PyPAImage("musllinux_1_2", "s390x", None),
-    PyPAImage("musllinux_1_2", "armv7l", None),
+    PyPAImage("musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l"]),
 ]
 
 config = configparser.ConfigParser()
@@ -137,10 +108,13 @@ def __init__(self, manylinux_version: str, platform: str, tag: str | None):
         )
         tag_name = pinned_tag["name"]
 
-    if not config.has_section(image.platform):
-        config[image.platform] = {}
-
-    config[image.platform][image.manylinux_version] = f"{image.image_name}:{tag_name}"
+    for platform in image.platforms:
+        if not config.has_section(platform):
+            config[platform] = {}
+        suffix = ""
+        if image.use_platform_suffix:
+            suffix = f"_{platform.removeprefix('pypy_')}"
+        config[platform][image.manylinux_version] = f"{image.image_name}{suffix}:{tag_name}"
 
 with open(RESOURCES / "pinned_docker_images.cfg", "w") as f:
     config.write(f)
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index fa7605343..5ab256232 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -673,10 +673,18 @@ def globals(self) -> GlobalOptions:
         )
 
     def _check_pinned_image(self, value: str, pinned_images: Mapping[str, str]) -> None:
-        if (
-            value in {"manylinux1", "manylinux2010", "manylinux_2_24", "musllinux_1_1"}
-            and value not in self._image_warnings
-        ):
+        error_set = {"manylinux1", "manylinux2010", "manylinux_2_24", "musllinux_1_1"}
+        warning_set: set[str] = set()
+
+        if value in error_set:
+            msg = (
+                f"cibuildwheel 3.x does not support the image {value!r}. Either upgrade to a "
+                "supported image or continue using the image by pinning it directly with"
+                " its full OCI registry '<name>{:<tag>|@<digest>}'."
+            )
+            raise errors.DeprecationError(msg)
+
+        if value in warning_set and value not in self._image_warnings:
             self._image_warnings.add(value)
             msg = (
                 f"Deprecated image {value!r}. This value will not work"
@@ -783,35 +791,29 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
 
                 for build_platform in MANYLINUX_ARCHS:
                     pinned_images = all_pinned_container_images[build_platform]
-
                     config_value = self.reader.get(
                         f"manylinux-{build_platform}-image", ignore_empty=True
                     )
-
+                    self._check_pinned_image(config_value, pinned_images)
                     if not config_value:
                         # default to manylinux2014
                         image = pinned_images["manylinux2014"]
                     elif config_value in pinned_images:
-                        self._check_pinned_image(config_value, pinned_images)
                         image = pinned_images[config_value]
                     else:
                         image = config_value
-
                     manylinux_images[build_platform] = image
 
                 for build_platform in MUSLLINUX_ARCHS:
                     pinned_images = all_pinned_container_images[build_platform]
-
                     config_value = self.reader.get(f"musllinux-{build_platform}-image")
-
+                    self._check_pinned_image(config_value, pinned_images)
                     if not config_value:
                         image = pinned_images["musllinux_1_2"]
                     elif config_value in pinned_images:
-                        self._check_pinned_image(config_value, pinned_images)
                         image = pinned_images[config_value]
                     else:
                         image = config_value
-
                     musllinux_images[build_platform] = image
 
             container_engine_str = self.reader.get(
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index a4e2e2964..7edb7380f 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,60 +1,41 @@
 [x86_64]
-manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
 manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.15-1
 manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.15-1
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.10.26-1
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.15-1
 
 [i686]
-manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
 manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.10.26-1
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.15-1
 
-[pypy_x86_64]
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.15-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.15-1
-
-[pypy_i686]
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
-
 [aarch64]
 manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.15-1
 manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.15-1
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.10.26-1
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.15-1
 
 [ppc64le]
 manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.15-1
 manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.15-1
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.10.26-1
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.15-1
 
 [s390x]
 manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.15-1
 manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.15-1
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.10.26-1
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.15-1
 
+[pypy_x86_64]
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.15-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.15-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.15-1
+
+[pypy_i686]
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.15-1
+
 [pypy_aarch64]
 manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.15-1
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.15-1
 manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.15-1
 
diff --git a/docs/cpp_standards.md b/docs/cpp_standards.md
index ac820300a..3b49481ee 100644
--- a/docs/cpp_standards.md
+++ b/docs/cpp_standards.md
@@ -7,10 +7,11 @@ title: Modern C++ standards
 Building Python wheels with modern C++ standards (C++11 and later) requires a few tricks.
 
 
-## manylinux1 and C++14
-The past end-of-life `manylinux1` image (based on CentOS 5) contains a version of GCC and libstdc++ that only supports C++11 and earlier standards. There are however ways to compile wheels with the C++14 standard (and later): https://github.com/pypa/manylinux/issues/118
+## manylinux2014 and C++20
 
-`manylinux2010` and `manylinux2014` are newer and support all C++ standards (up to C++17).
+The past end-of-life `manylinux2014` image (based on CentOS 7) contains a version of GCC and libstdc++ that only supports C++17 and earlier standards.
+
+`manylinux_2_28` are newer and support all C++ standards (up to C++20).
 
 ## macOS and deployment target versions
 
diff --git a/docs/faq.md b/docs/faq.md
index 8e61dba21..81b161715 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -10,7 +10,7 @@ title: Tips and tricks
 
 Linux wheels are built in [`manylinux`/`musllinux` containers](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 600](https://www.python.org/dev/peps/pep-0600/) / [PEP 656](https://www.python.org/dev/peps/pep-0656/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
 
--   Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using `yum` for `manylinux2010` or `manylinux2014`, `apt-get` for `manylinux_2_24`/`manylinux_2_31`, `dnf` for `manylinux_2_28` and `apk` for `musllinux_1_1` or `musllinux_1_2`, or manually. The same goes for environment variables that are potentially needed to customize the wheel building.
+-   Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using `yum` for `manylinux2014`, `apt-get` for `manylinux_2_31`, `dnf` for `manylinux_2_28` and `apk` for `musllinux_1_1` or `musllinux_1_2`, or manually. The same goes for environment variables that are potentially needed to customize the wheel building.
 
     `cibuildwheel` supports this by providing the [`CIBW_ENVIRONMENT`](options.md#environment) and [`CIBW_BEFORE_ALL`](options.md#before-all) options to setup the build environment inside the running container.
 
diff --git a/docs/options.md b/docs/options.md
index dd90e9fcb..2e8a199c7 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -158,19 +158,19 @@ which means it can be given multiple times.
 ```toml
 [tool.cibuildwheel]
 # Normal options, etc.
-manylinux-x86_64-image = "manylinux2014"
+manylinux-x86_64-image = "manylinux_2_34"
 
 [[tool.cibuildwheel.overrides]]
 select = "cp38-*"
-manylinux-x86_64-image = "manylinux1"
+manylinux-x86_64-image = "manylinux2014"
 
 [[tool.cibuildwheel.overrides]]
 select = "cp3{9,10}-*"
-manylinux-x86_64-image = "manylinux2010"
+manylinux-x86_64-image = "manylinux_2_28"
 ```
 
-This example will build CPython 3.8 wheels on manylinux1, CPython 3.9-3.10
-wheels on manylinux2010, and manylinux2014 wheels for any newer Python
+This example will build CPython 3.8 wheels on manylinux2014, CPython 3.9-3.10
+wheels on manylinux_2_28, and manylinux_2_34 wheels for any newer Python
 (like 3.10).
 
 ```toml
@@ -955,10 +955,9 @@ Platform-specific environment variables also available:<br/>
 
     In configuration files, you can use a TOML array, and each line will be run sequentially - joined with `&&`.
 
-Note that manylinux_2_24/manylinux_2_31 builds occur inside a debian derivative docker container, where
-manylinux2010 and manylinux2014 builds occur inside a CentOS one. So for
-`manylinux_2_24`/`manylinux_2_31` the `CIBW_BEFORE_ALL_LINUX` command must use `apt-get -y`
-instead.
+Note that manylinux_2_31 builds occur inside a debian derivative docker container, where
+manylinux2014 builds occur inside a CentOS one. So for `manylinux_2_31` the `CIBW_BEFORE_ALL_LINUX` command
+must use `apt-get -y` instead.
 
 ### `CIBW_BEFORE_BUILD` {: #before-build}
 > Execute a shell command preparing each wheel's build
@@ -1191,17 +1190,18 @@ The available options are:
 
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
 
-For `CIBW_MANYLINUX_*_IMAGE`, except `CIBW_MANYLINUX_ARMV7L_IMAGE`, the value of this option can either be set to `manylinux1`, `manylinux2010`, `manylinux2014`, `manylinux_2_24` or `manylinux_2_28` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name. For PyPy, the `manylinux1` image is not available. For architectures other
-than x86 (x86\_64 and i686) `manylinux2014`, `manylinux_2_24`, `manylinux_2_28` or `manylinux_2_34` must be used, because the first version of the manylinux specification that supports additional architectures is `manylinux2014`. `manylinux_2_28` and `manylinux_2_34` are not supported for `i686` architecture.
+For `CIBW_MANYLINUX_*_IMAGE`, except `CIBW_MANYLINUX_ARMV7L_IMAGE`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
+`manylinux_2_28` and `manylinux_2_34` are not supported for `i686` architecture.
+
 For `CIBW_MANYLINUX_ARMV7L_IMAGE`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
 
-For `CIBW_MUSLLINUX_*_IMAGE`, the value of this option can either be set to `musllinux_1_1` or `musllinux_1_2` to use a pinned version of the [official musllinux images](https://github.com/pypa/musllinux). Alternatively, set these options to any other valid Docker image name.
+For `CIBW_MUSLLINUX_*_IMAGE`, the value of this option can either be set to `musllinux_1_2` or a custom image.
 
 If this option is blank, it will fall though to the next available definition (environment variable -> pyproject.toml -> default).
 
-If setting a custom image, you'll need to make sure it can be used in the same way as the default images: all necessary Python and pip versions need to be present in `/opt/python/`, and the auditwheel tool needs to be present for cibuildwheel to work. Apart from that, the architecture and relevant shared system libraries need to be compatible to the relevant standard to produce valid manylinux1/manylinux2010/manylinux2014/manylinux_2_24/manylinux_2_28/manylinux_2_34/musllinux_1_1/musllinux_1_2 wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 513](https://www.python.org/dev/peps/pep-0513/), [PEP 571](https://www.python.org/dev/peps/pep-0571/), [PEP 599](https://www.python.org/dev/peps/pep-0599/), [PEP 600](https://www.python.org/dev/peps/pep-0600/) and [PEP 656](https://www.python.org/dev/peps/pep-0656/) for more details).
+If setting a custom image, you'll need to make sure it can be used in the same way as the default images: all necessary Python and pip versions need to be present in `/opt/python/`, and the auditwheel tool needs to be present for cibuildwheel to work. Apart from that, the architecture and relevant shared system libraries need to be compatible to the relevant standard to produce valid manylinux2014/manylinux_2_28/manylinux_2_34/musllinux_1_2 wheels (see [pypa/manylinux on GitHub](https://github.com/pypa/manylinux), [PEP 599](https://www.python.org/dev/peps/pep-0599/), [PEP 600](https://www.python.org/dev/peps/pep-0600/) and [PEP 656](https://www.python.org/dev/peps/pep-0656/) for more details).
 
-Auditwheel detects the version of the manylinux / musllinux standard in the image through the `AUDITWHEEL_PLAT` environment variable, as cibuildwheel has no way of detecting the correct `--plat` command line argument to pass to auditwheel for a custom image. If a custom image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT='AUDITWHEEL_PLAT="manylinux2010_$(uname -m)"'`).
+Auditwheel detects the version of the manylinux / musllinux standard in the image through the `AUDITWHEEL_PLAT` environment variable, as cibuildwheel has no way of detecting the correct `--plat` command line argument to pass to auditwheel for a custom image. If a custom image does not correctly set this `AUDITWHEEL_PLAT` environment variable, the `CIBW_ENVIRONMENT` option can be used to do so (e.g., `CIBW_ENVIRONMENT='AUDITWHEEL_PLAT="manylinux2014_$(uname -m)"'`).
 
 !!! warning
     On x86_64, `manylinux_2_34` is using [x86-64-v2](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels) target architecture.
@@ -1224,50 +1224,38 @@ Auditwheel detects the version of the manylinux / musllinux standard in the imag
 !!! tab examples "Environment variables"
 
     ```yaml
-    # Build using the manylinux1 image to ensure manylinux1 wheels are produced
-    # Not setting PyPy to manylinux1, since there is no manylinux1 PyPy image.
-    CIBW_MANYLINUX_X86_64_IMAGE: manylinux1
-    CIBW_MANYLINUX_I686_IMAGE: manylinux1
-
     # Build using the manylinux2014 image
     CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
     CIBW_MANYLINUX_I686_IMAGE: manylinux2014
     CIBW_MANYLINUX_PYPY_X86_64_IMAGE: manylinux2014
     CIBW_MANYLINUX_PYPY_I686_IMAGE: manylinux2014
 
-    # Build using the latest manylinux2014 release, instead of the cibuildwheel
-    # pinned version
-    CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux2014_x86_64:latest
-    CIBW_MANYLINUX_I686_IMAGE: quay.io/pypa/manylinux2014_i686:latest
-    CIBW_MANYLINUX_PYPY_X86_64_IMAGE: quay.io/pypa/manylinux2014_x86_64:latest
-    CIBW_MANYLINUX_PYPY_I686_IMAGE: quay.io/pypa/manylinux2014_i686:latest
+    # Build using the latest manylinux2010 release
+    CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
+    CIBW_MANYLINUX_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
+    CIBW_MANYLINUX_PYPY_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
+    CIBW_MANYLINUX_PYPY_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
 
     # Build using a different image from the docker registry
     CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
     CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
 
     # Build musllinux wheels using the musllinux_1_1 image
-    CIBW_MUSLLINUX_X86_64_IMAGE: musllinux_1_1
-    CIBW_MUSLLINUX_I686_IMAGE: musllinux_1_1
+    CIBW_MUSLLINUX_X86_64_IMAGE: quay.io/pypa/musllinux_1_1_x86_64:latest
+    CIBW_MUSLLINUX_I686_IMAGE: quay.io/pypa/musllinux_1_1_i686:latest
     ```
 
 !!! tab examples "pyproject.toml"
 
     ```toml
     [tool.cibuildwheel]
-    # Build using the manylinux1 image to ensure manylinux1 wheels are produced
-    # Not setting PyPy to manylinux1, since there is no manylinux1 PyPy image.
-    manylinux-x86_64-image = "manylinux1"
-    manylinux-i686-image = "manylinux1"
-
     # Build using the manylinux2014 image
     manylinux-x86_64-image = "manylinux2014"
     manylinux-i686-image = "manylinux2014"
     manylinux-pypy_x86_64-image = "manylinux2014"
     manylinux-pypy_i686-image = "manylinux2014"
 
-    # Build using the latest manylinux2010 release, instead of the cibuildwheel
-    # pinned version
+    # Build using the latest manylinux2010 release
     manylinux-x86_64-image = "quay.io/pypa/manylinux2010_x86_64:latest"
     manylinux-i686-image = "quay.io/pypa/manylinux2010_i686:latest"
     manylinux-pypy_x86_64-image = "quay.io/pypa/manylinux2010_x86_64:latest"
@@ -1278,8 +1266,8 @@ Auditwheel detects the version of the manylinux / musllinux standard in the imag
     manylinux-i686-image = "dockcross/manylinux-x86"
 
     # Build musllinux wheels using the musllinux_1_1 image
-    musllinux-x86_64-image = "musllinux_1_1"
-    musllinux-i686-image = "musllinux_1_1"
+    musllinux-x86_64-image = "quay.io/pypa/musllinux_1_1_x86_64:latest"
+    musllinux-i686-image = "quay.io/pypa/musllinux_1_1_i686:latest"
     ```
 
     Like any other option, these can be placed in `[tool.cibuildwheel.linux]`
diff --git a/test/test_container_images.py b/test/test_container_images.py
index 60517a0f7..17626a2ef 100644
--- a/test/test_container_images.py
+++ b/test/test_container_images.py
@@ -11,7 +11,7 @@
         import os
 
         # check that we're running in the correct docker image as specified in the
-        # environment options CIBW_MANYLINUX1_*_IMAGE
+        # environment options CIBW_MANYLINUX_*_IMAGE
         if "linux" in sys.platform and not os.path.exists("/dockcross"):
             raise Exception(
                 "/dockcross directory not found. Is this test running in the correct docker image?"
diff --git a/test/test_linux_python.py b/test/test_linux_python.py
index c4e7f4e53..7c5f58a56 100644
--- a/test/test_linux_python.py
+++ b/test/test_linux_python.py
@@ -19,12 +19,14 @@ def test_python_exist(tmp_path, capfd):
     basic_project = test_projects.new_c_project()
     basic_project.generate(project_dir)
 
+    image = f"quay.io/pypa/manylinux2010_{machine}:2022-08-05-4535177"
+
     with pytest.raises(subprocess.CalledProcessError):
         utils.cibuildwheel_run(
             project_dir,
             add_env={
-                "CIBW_MANYLINUX_X86_64_IMAGE": "manylinux2010",
-                "CIBW_MANYLINUX_I686_IMAGE": "manylinux2010",
+                "CIBW_MANYLINUX_X86_64_IMAGE": image,
+                "CIBW_MANYLINUX_I686_IMAGE": image,
                 "CIBW_BUILD": "cp3{10,11}-manylinux*",
             },
         )
@@ -35,7 +37,6 @@ def test_python_exist(tmp_path, capfd):
     assert f" to build 'cp310-manylinux_{machine}'." not in captured.err
     message = (
         "'/opt/python/cp311-cp311/bin/python' executable doesn't exist"
-        f" in image 'quay.io/pypa/manylinux2010_{machine}:2022-08-05-4535177'"
-        f" to build 'cp311-manylinux_{machine}'."
+        f" in image '{image}' to build 'cp311-manylinux_{machine}'."
     )
     assert message in captured.err
diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py
index 41e78ad3d..c8d2c6d99 100644
--- a/test/test_manylinuxXXXX_only.py
+++ b/test/test_manylinuxXXXX_only.py
@@ -19,7 +19,7 @@
         #error "Must run on a glibc linux environment"
         #endif
 
-        #if !__GLIBC_PREREQ(2, 5)  /* manylinux1 is glibc 2.5 */
+        #if !__GLIBC_PREREQ(2, 17)  /* manylinux2014 is glibc 2.17 */
         #error "Must run on a glibc >= 2.5 linux environment"
         #endif
 
@@ -46,9 +46,6 @@
         #elif __GLIBC_PREREQ(2, 17)  /* manylinux2014 is glibc 2.17 */
             // secure_getenv is only available in manylinux2014+
             sts = (int)(intptr_t)secure_getenv("NON_EXISTING_ENV_VARIABLE");
-        #elif __GLIBC_PREREQ(2, 10)  /* manylinux2010 is glibc 2.12 */
-            // malloc_info is only available on manylinux2010+
-            sts = malloc_info(0, stdout);
         #endif
         """
     ),
@@ -58,10 +55,7 @@
 @pytest.mark.parametrize(
     "manylinux_image",
     [
-        "manylinux1",
-        "manylinux2010",
         "manylinux2014",
-        "manylinux_2_24",
         "manylinux_2_28",
         "manylinux_2_34",
     ],
@@ -70,9 +64,6 @@
 def test(manylinux_image, tmp_path):
     if utils.platform != "linux":
         pytest.skip("the container image test is only relevant to the linux build")
-    elif platform.machine() not in {"x86_64", "i686"}:
-        if manylinux_image in {"manylinux1", "manylinux2010"}:
-            pytest.skip(f"{manylinux_image} doesn't exist for non-x86 architectures")
     elif manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "i686":
         pytest.skip(f"{manylinux_image} doesn't exist for i686 architecture")
 
@@ -80,7 +71,7 @@ def test(manylinux_image, tmp_path):
     project_with_manylinux_symbols.generate(project_dir)
 
     # build the wheels
-    # CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
+    # CFLAGS environment variable is necessary to fail at build time,
     # rather than when dynamically loading the Python
     add_env = {
         "CIBW_BUILD": "*-manylinux*",
@@ -94,15 +85,6 @@ def test(manylinux_image, tmp_path):
         "CIBW_MANYLINUX_PYPY_AARCH64_IMAGE": manylinux_image,
         "CIBW_MANYLINUX_PYPY_I686_IMAGE": manylinux_image,
     }
-    if manylinux_image == "manylinux1":
-        # We don't have a manylinux1 image for PyPy & CPython 3.10 and above
-        add_env["CIBW_SKIP"] = "pp* cp31*"
-    if manylinux_image == "manylinux2010":
-        # We don't have a manylinux2010 image for PyPy 3.9+, CPython 3.11+
-        add_env["CIBW_SKIP"] = "pp39* pp31* cp311* cp312* cp313*"
-    if manylinux_image == "manylinux_2_24":
-        # We don't have a manylinux_2_24 image for PyPy 3.10+, CPython 3.12+
-        add_env["CIBW_SKIP"] = "pp31* cp312* cp313*"
     if manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "x86_64":
         # We don't have a manylinux_2_28+ image for i686
         add_env["CIBW_ARCHS"] = "x86_64"
@@ -113,8 +95,6 @@ def test(manylinux_image, tmp_path):
     actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
 
     platform_tag_map = {
-        "manylinux1": ["manylinux_2_5", "manylinux1"],
-        "manylinux2010": ["manylinux_2_12", "manylinux2010"],
         "manylinux2014": ["manylinux_2_17", "manylinux2014"],
     }
     expected_wheels = utils.expected_wheels(
@@ -123,29 +103,6 @@ def test(manylinux_image, tmp_path):
         manylinux_versions=platform_tag_map.get(manylinux_image, [manylinux_image]),
         musllinux_versions=[],
     )
-    if manylinux_image == "manylinux1":
-        # remove PyPy & CPython 3.10 and above
-        expected_wheels = [w for w in expected_wheels if "-pp" not in w and "-cp31" not in w]
-
-    if manylinux_image == "manylinux2010":
-        # remove PyPy 3.9+ & CPython 3.11
-        expected_wheels = [
-            w
-            for w in expected_wheels
-            if "-pp39" not in w
-            and "-pp31" not in w
-            and "-cp311" not in w
-            and "-cp312" not in w
-            and "-cp313" not in w
-        ]
-
-    if manylinux_image == "manylinux_2_24":
-        # remove PyPy 3.10+ & CPython 3.11 and above
-        expected_wheels = [
-            w
-            for w in expected_wheels
-            if "-pp31" not in w and "-cp312" not in w and "-cp313" not in w
-        ]
 
     if manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "x86_64":
         # We don't have a manylinux_2_28+ image for i686
diff --git a/test/test_musllinux_X_Y_only.py b/test/test_musllinux_X_Y_only.py
index 9438821a1..f8396aa6a 100644
--- a/test/test_musllinux_X_Y_only.py
+++ b/test/test_musllinux_X_Y_only.py
@@ -24,7 +24,7 @@
 
 @pytest.mark.parametrize(
     "musllinux_image",
-    ["musllinux_1_1", "musllinux_1_2"],
+    ["musllinux_1_2"],
 )
 @pytest.mark.usefixtures("docker_cleanup")
 def test(musllinux_image, tmp_path):
@@ -36,12 +36,13 @@ def test(musllinux_image, tmp_path):
 
     # build the wheels
     add_env = {
-        "CIBW_SKIP": "*-manylinux* *_armv7l",
+        "CIBW_SKIP": "*-manylinux*",
         "CIBW_MUSLLINUX_X86_64_IMAGE": musllinux_image,
         "CIBW_MUSLLINUX_I686_IMAGE": musllinux_image,
         "CIBW_MUSLLINUX_AARCH64_IMAGE": musllinux_image,
         "CIBW_MUSLLINUX_PPC64LE_IMAGE": musllinux_image,
         "CIBW_MUSLLINUX_S390X_IMAGE": musllinux_image,
+        "CIBW_MUSLLINUX_ARMV7L_IMAGE": musllinux_image,
     }
 
     actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env, single_python=True)
@@ -52,5 +53,4 @@ def test(musllinux_image, tmp_path):
         musllinux_versions=[musllinux_image],
         single_python=True,
     )
-    expected_wheels = [w for w in expected_wheels if "armv7l" not in w]
     assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/test_testing.py b/test/test_testing.py
index 8bd810f50..a4bae870c 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -167,10 +167,6 @@ def test_failing_test(tmp_path):
             add_env={
                 "CIBW_TEST_REQUIRES": "pytest",
                 "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} {{project}}/test",
-                # manylinux1 has a version of bash that's been shown to have
-                # problems with this, so let's check that.
-                "CIBW_MANYLINUX_I686_IMAGE": "manylinux1",
-                "CIBW_MANYLINUX_X86_64_IMAGE": "manylinux1",
                 # CPython 3.8 when running on macOS arm64 is unusual. The build
                 # always runs in x86_64, so the arm64 tests are not run. See
                 # #1169 for reasons why. That means the build succeeds, which
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index 2aaebda43..a869d615f 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -80,24 +80,15 @@ def test_empty_selector(monkeypatch):
     ("architecture", "image", "full_image"),
     [
         ("x86_64", None, "quay.io/pypa/manylinux2014_x86_64:*"),
-        ("x86_64", "manylinux1", "quay.io/pypa/manylinux1_x86_64:*"),
-        ("x86_64", "manylinux2010", "quay.io/pypa/manylinux2010_x86_64:*"),
         ("x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
-        ("x86_64", "manylinux_2_24", "quay.io/pypa/manylinux_2_24_x86_64:*"),
         ("x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
         ("x86_64", "custom_image", "custom_image"),
         ("i686", None, "quay.io/pypa/manylinux2014_i686:*"),
-        ("i686", "manylinux1", "quay.io/pypa/manylinux1_i686:*"),
-        ("i686", "manylinux2010", "quay.io/pypa/manylinux2010_i686:*"),
         ("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"),
-        ("i686", "manylinux_2_24", "quay.io/pypa/manylinux_2_24_i686:*"),
         ("i686", "custom_image", "custom_image"),
         ("pypy_x86_64", None, "quay.io/pypa/manylinux2014_x86_64:*"),
-        ("pypy_x86_64", "manylinux1", "manylinux1"),  # Does not exist
-        ("pypy_x86_64", "manylinux2010", "quay.io/pypa/manylinux2010_x86_64:*"),
         ("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
-        ("pypy_x86_64", "manylinux_2_24", "quay.io/pypa/manylinux_2_24_x86_64:*"),
         ("pypy_x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("pypy_x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
         ("pypy_x86_64", "custom_image", "custom_image"),
diff --git a/unit_test/oci_container_test.py b/unit_test/oci_container_test.py
index 14c635b31..5acc9cc48 100644
--- a/unit_test/oci_container_test.py
+++ b/unit_test/oci_container_test.py
@@ -28,14 +28,8 @@
 
 # for these tests we use manylinux2014 images, because they're available on
 # multi architectures and include python3.8
-DEFAULT_IMAGE_TEMPLATE = "quay.io/pypa/manylinux2014_{machine}:2023-09-04-0828984"
+DEFAULT_IMAGE = "quay.io/pypa/manylinux2014:2025.03.08-1"
 pm = platform.machine()
-if pm in {"x86_64", "ppc64le", "s390x"}:
-    DEFAULT_IMAGE = DEFAULT_IMAGE_TEMPLATE.format(machine=pm)
-elif pm in {"aarch64", "arm64"}:
-    DEFAULT_IMAGE = DEFAULT_IMAGE_TEMPLATE.format(machine="aarch64")
-else:
-    DEFAULT_IMAGE = ""
 DEFAULT_OCI_PLATFORM = {
     "AMD64": OCIPlatform.AMD64,
     "x86_64": OCIPlatform.AMD64,
@@ -415,9 +409,7 @@ def test_create_args_volume(tmp_path: Path, container_engine: OCIContainerEngine
     )
 
     with OCIContainer(
-        engine=container_engine,
-        image=DEFAULT_IMAGE,
-        oci_platform=DEFAULT_OCI_PLATFORM,
+        engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
     ) as container:
         assert container.call(["cat", "/test_mount/test_file.txt"], capture_output=True) == "1234"
 
@@ -497,9 +489,7 @@ def test_parse_engine_config(config, name, create_args, capsys):
 @pytest.mark.skipif(pm != "x86_64", reason="Only runs on x86_64")
 def test_enforce_32_bit(container_engine):
     with OCIContainer(
-        engine=container_engine,
-        image=DEFAULT_IMAGE_TEMPLATE.format(machine="i686"),
-        oci_platform=OCIPlatform.i386,
+        engine=container_engine, image=DEFAULT_IMAGE, oci_platform=OCIPlatform.i386
     ) as container:
         assert container.call(["uname", "-m"], capture_output=True).strip() == "i686"
         container_args = subprocess.run(
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 995048b2c..27b25377c 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -104,12 +104,11 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 musllinux-x86_64-image = "musllinux_1_2"
 enable = ["pypy", "cpython-freethreading"]
 
-# Before Python 3.10, use manylinux2014, musllinux_1_1
+# Before Python 3.10, use manylinux2014
 [[tool.cibuildwheel.overrides]]
 select = "cp3?-*"
 manylinux-x86_64-image = "manylinux2014"
 manylinux-i686-image = "manylinux2014"
-musllinux-x86_64-image = "musllinux_1_1"
 
 [[tool.cibuildwheel.overrides]]
 select = "cp38-manylinux_x86_64"
@@ -124,7 +123,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 
     build_in_container = typing.cast(mock.Mock, linux.build_in_container)
 
-    assert build_in_container.call_count == 7
+    assert build_in_container.call_count == 6
 
     kwargs = build_in_container.call_args_list[0][1]
     assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
@@ -188,25 +187,13 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
     assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS}
 
     kwargs = build_in_container.call_args_list[4][1]
-    assert "quay.io/pypa/musllinux_1_1_x86_64" in kwargs["container"]["image"]
-    assert kwargs["container"]["cwd"] == PurePosixPath("/project")
-    assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
-
-    identifiers = {x.identifier for x in kwargs["platform_configs"]}
-    assert identifiers == {
-        f"{x}-musllinux_x86_64" for x in ALL_IDS & {"cp38", "cp39"} if "pp" not in x
-    }
-
-    kwargs = build_in_container.call_args_list[5][1]
     assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
     identifiers = {x.identifier for x in kwargs["platform_configs"]}
-    assert identifiers == {
-        f"{x}-musllinux_x86_64" for x in ALL_IDS - {"cp38", "cp39"} if "pp" not in x
-    }
+    assert identifiers == {f"{x}-musllinux_x86_64" for x in ALL_IDS if "pp" not in x}
 
-    kwargs = build_in_container.call_args_list[6][1]
+    kwargs = build_in_container.call_args_list[5][1]
     assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index 499862a99..7f497a375 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -26,7 +26,7 @@
 test-command = "pyproject"
 test-sources = ["test", "other dir"]
 
-manylinux-x86_64-image = "manylinux1"
+manylinux-x86_64-image = "manylinux_2_28"
 
 environment-pass = ["EXAMPLE_ENV"]
 
@@ -36,7 +36,7 @@
 [[tool.cibuildwheel.overrides]]
 select = "cp313-*"
 test-command = "pyproject-override"
-manylinux-x86_64-image = "manylinux2014"
+manylinux-x86_64-image = "manylinux_2_34"
 """
 
 
@@ -76,13 +76,13 @@ def test_options_1(tmp_path, monkeypatch):
     assert local.manylinux_images is not None
     assert local.test_command == "pyproject"
     assert local.test_sources == ["test", "other dir"]
-    assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux1"]
+    assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux_2_28"]
 
     local = options.build_options("cp313-manylinux_x86_64")
     assert local.manylinux_images is not None
     assert local.test_command == "pyproject-override"
     assert local.test_sources == ["test", "other dir"]
-    assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux2014"]
+    assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux_2_34"]
 
 
 def test_passthrough(tmp_path, monkeypatch):
@@ -532,19 +532,22 @@ def test_dependency_versions_toml(
 
 
 @pytest.mark.parametrize(
-    ("image", "deprecated"),
+    ("image", "deprecated", "raises"),
     [
-        ("manylinux1", True),
-        ("manylinux2010", True),
-        ("manylinux2014", False),
-        ("manylinux_2_24", True),
-        ("manylinux_2_28", False),
-        ("manylinux_2_34", False),
-        ("musllinux_1_1", True),
-        ("musllinux_1_2", False),
+        ("manylinux1", True, True),
+        ("manylinux2010", True, True),
+        ("manylinux2014", False, False),
+        ("manylinux_2_24", True, True),
+        ("manylinux_2_28", False, False),
+        ("manylinux_2_34", False, False),
+        ("musllinux_1_1", True, True),
+        ("musllinux_1_2", False, False),
     ],
 )
-def test_deprecated_image(image: str, deprecated: bool, capsys: pytest.CaptureFixture[str]) -> None:
+def test_deprecated_image(
+    image: str, deprecated: bool, raises: bool, capsys: pytest.CaptureFixture[str]
+) -> None:
+    assert not raises or deprecated
     args = CommandLineArguments.defaults()
     env = {
         "CIBW_ARCHS": "x86_64",
@@ -552,7 +555,12 @@ def test_deprecated_image(image: str, deprecated: bool, capsys: pytest.CaptureFi
         "CIBW_MUSLLINUX_X86_64_IMAGE": image if image.startswith("musllinux") else "",
     }
     options = Options(platform="linux", command_line_arguments=args, env=env)
-    bo = options.build_options(None)
+    try:
+        bo = options.build_options(None)
+        assert not raises
+    except errors.DeprecationError:
+        assert raises
+        return
     images = bo.manylinux_images if image.startswith("manylinux") else bo.musllinux_images
     assert images is not None
     resolved_image = images["x86_64"]
diff --git a/unit_test/options_toml_test.py b/unit_test/options_toml_test.py
index 749ff4b50..03e125013 100644
--- a/unit_test/options_toml_test.py
+++ b/unit_test/options_toml_test.py
@@ -24,7 +24,7 @@
 test-groups = ["three", "four"]
 test-sources = ["five", "six and seven"]
 
-manylinux-x86_64-image = "manylinux1"
+manylinux-x86_64-image = "manylinux_2_28"
 
 [tool.cibuildwheel.macos]
 test-requires = "else"
@@ -67,7 +67,7 @@ def test_simple_settings(tmp_path, platform, fname):
     assert options_reader.get("test-extras", option_format=ListFormat(",")) == "one,two"
     assert options_reader.get("test-groups", option_format=ListFormat(" ")) == "three four"
 
-    assert options_reader.get("manylinux-x86_64-image") == "manylinux1"
+    assert options_reader.get("manylinux-x86_64-image") == "manylinux_2_28"
     assert options_reader.get("manylinux-i686-image") == "manylinux2014"
 
     with pytest.raises(OptionsReaderError):

From 2efa648f38e83a421aae82bc80002f8cabf92be7 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sun, 23 Mar 2025 15:03:30 -0400
Subject: [PATCH 022/193] fix: always resolve --python argument (#2328) (#2331)

fix: always resolve --python argument (#2328)

fix: always resolve the --python argument

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/util.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py
index 905dd5410..739f77579 100644
--- a/cibuildwheel/util.py
+++ b/cibuildwheel/util.py
@@ -705,6 +705,10 @@ def virtualenv(
     dependency_constraint_flags are ignored since nothing is installed in the
     venv. Otherwise, pip is installed, and setuptools + wheel if Python < 3.12.
     """
+
+    # virtualenv may fail if this is a symlink.
+    python = python.resolve()
+
     assert python.exists()
 
     if use_uv:

From 4db404ca50b222e27fa84a9d3e4bdc7a26cc8a40 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 24 Mar 2025 11:50:40 -0400
Subject: [PATCH 023/193] [Bot] Update dependencies (#2332)

* Update dependencies

* fix: stop producing `cibuildwheel/resources/constraints-python37.txt`

---------

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   |  6 +--
 .../resources/constraints-pyodide312.txt      |  2 +-
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 docs/working-examples.md                      | 16 +++---
 noxfile.py                                    |  2 +-
 11 files changed, 46 insertions(+), 46 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index b5fa071a2..18838f15c 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -166,7 +166,7 @@ python_configurations = [
 
 [ios]
 python_configurations = [
-  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b5/Python-3.13-iOS-support.b5.tar.gz" },
-  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b5/Python-3.13-iOS-support.b5.tar.gz" },
-  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b5/Python-3.13-iOS-support.b5.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" },
+  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" },
 ]
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 59f7c98e9..7827ad07e 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -50,7 +50,7 @@ packaging==24.2
     #   unearth
 pip==25.0.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pydantic==2.10.6
     # via
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index e3b1e97a2..c7cd2fb8e 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -20,7 +20,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index f6dbd9b8d..7b07dba10 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index f6dbd9b8d..7b07dba10 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index f6dbd9b8d..7b07dba10 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index e3b1e97a2..c7cd2fb8e 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -20,7 +20,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index f6dbd9b8d..7b07dba10 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 7edb7380f..82c484a71 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,45 +1,45 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.15-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.15-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.15-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.23-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.15-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.23-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.15-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.15-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.15-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.23-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.15-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.15-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.15-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.23-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.15-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.15-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.15-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.23-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.15-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.15-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.23-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.15-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.15-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.15-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.23-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.15-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.15-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.23-1
 
diff --git a/docs/working-examples.md b/docs/working-examples.md
index e960100bc..9ed006bca 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -70,8 +70,8 @@ title: Working examples
 | [time-machine][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Time mocking library using only the CPython C API. |
 | [Picologging][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. |
 | [pybind11 cmake_example][]        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system |
-| [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
 | [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
+| [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
@@ -85,10 +85,10 @@ title: Working examples
 | [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [Tokenizer][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
 | [iminuit][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
-| [keyvi][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime |
 | [pillow-heif][]                   | ![github icon][] ![cirrusci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Bindings to libheif library with third party dependencies. Fully automated CI for tests and publishing including Apple Silicon builds. |
-| [PyGLM][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python |
+| [keyvi][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime |
 | [Confluent client for Kafka][]    | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
+| [PyGLM][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python |
 | [TgCrypto][]                      | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. |
 | [iDynTree][]                      | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 |
 | [streaming-form-data][]           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython |
@@ -108,8 +108,8 @@ title: Working examples
 | [ninja][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
 | [numpythia][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
 | [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
-| [ril][]                           | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
 | [SiPM][]                          | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
+| [ril][]                           | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
 | [GSD][]                           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
 | [aalink][]                        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
 | [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
@@ -180,8 +180,8 @@ title: Working examples
 [time-machine]: https://github.com/adamchainz/time-machine
 [Picologging]: https://github.com/microsoft/picologging
 [pybind11 cmake_example]: https://github.com/pybind/cmake_example
-[Rtree]: https://github.com/Toblerity/rtree
 [markupsafe]: https://github.com/pallets/markupsafe
+[Rtree]: https://github.com/Toblerity/rtree
 [KDEpy]: https://github.com/tommyod/KDEpy
 [dd-trace-py]: https://github.com/DataDog/dd-trace-py
 [tgcalls]: https://github.com/MarshalX/tgcalls
@@ -195,10 +195,10 @@ title: Working examples
 [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [Tokenizer]: https://github.com/OpenNMT/Tokenizer
 [iminuit]: https://github.com/scikit-hep/iminuit
-[keyvi]: https://github.com/KeyviDev/keyvi
 [pillow-heif]: https://github.com/bigcat88/pillow_heif
-[PyGLM]: https://github.com/Zuzu-Typ/PyGLM
+[keyvi]: https://github.com/KeyviDev/keyvi
 [Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
+[PyGLM]: https://github.com/Zuzu-Typ/PyGLM
 [TgCrypto]: https://github.com/pyrogram/tgcrypto
 [iDynTree]: https://github.com/robotology/idyntree
 [streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data
@@ -218,8 +218,8 @@ title: Working examples
 [ninja]: https://github.com/scikit-build/ninja-python-distributions
 [numpythia]: https://github.com/scikit-hep/numpythia
 [pyjet]: https://github.com/scikit-hep/pyjet
-[ril]: https://github.com/Cryptex-github/ril-py
 [SiPM]: https://github.com/EdoPro98/SimSiPM
+[ril]: https://github.com/Cryptex-github/ril-py
 [GSD]: https://github.com/glotzerlab/gsd
 [aalink]: https://github.com/artfwo/aalink
 [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
diff --git a/noxfile.py b/noxfile.py
index 235d15fc3..356bb8946 100755
--- a/noxfile.py
+++ b/noxfile.py
@@ -73,7 +73,7 @@ def update_constraints(session: nox.Session) -> None:
     if session.venv_backend != "uv":
         session.install("uv>=0.1.23")
 
-    for minor_version in range(7, 14):
+    for minor_version in range(8, 14):
         python_version = f"3.{minor_version}"
         env = os.environ.copy()
         # CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to

From 141e7028727d29f743ac0f3f47ccbec22b287512 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 24 Mar 2025 16:58:47 +0000
Subject: [PATCH 024/193] feat: stricter selector parsing, refactor to
 `platforms` module (#2291)

* feat: stricter selector parsing, refactor to `platforms` module

- Use a different method to build nothing
- Make the check aware of enable groups

* optimise unit tests - a specific platform module API for all configs

Unit test time: 26.2s -> 13.1s

* Remove unnecessary get_platform_module function
---
 cibuildwheel/__main__.py                  | 118 ++++++++++++++--------
 cibuildwheel/options.py                   |  25 +----
 cibuildwheel/platforms/__init__.py        |  41 ++++++++
 cibuildwheel/{ => platforms}/ios.py       |  44 ++++----
 cibuildwheel/{ => platforms}/linux.py     |  33 +++---
 cibuildwheel/{ => platforms}/macos.py     |  39 +++----
 cibuildwheel/{ => platforms}/pyodide.py   |  39 +++----
 cibuildwheel/{ => platforms}/windows.py   |  37 +++----
 cibuildwheel/selector.py                  |   8 +-
 test/test_0_basic.py                      |   2 +-
 test/utils.py                             |   3 +-
 unit_test/get_platform_test.py            |   2 +-
 unit_test/linux_build_steps_test.py       |  14 +--
 unit_test/main_tests/conftest.py          |   3 +-
 unit_test/main_tests/main_options_test.py |  43 ++++++--
 unit_test/option_prepare_test.py          |  11 +-
 unit_test/options_test.py                 |   4 +-
 17 files changed, 283 insertions(+), 183 deletions(-)
 create mode 100644 cibuildwheel/platforms/__init__.py
 rename cibuildwheel/{ => platforms}/ios.py (97%)
 rename cibuildwheel/{ => platforms}/linux.py (96%)
 rename cibuildwheel/{ => platforms}/macos.py (97%)
 rename cibuildwheel/{ => platforms}/pyodide.py (95%)
 rename cibuildwheel/{ => platforms}/windows.py (96%)

diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index e16adca9c..eded443de 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -9,25 +9,21 @@
 import time
 import traceback
 import typing
-from collections.abc import Generator, Iterable, Sequence, Set
+from collections.abc import Generator, Iterable, Sequence
 from pathlib import Path
 from tempfile import mkdtemp
-from typing import Any, Protocol, TextIO, assert_never
+from typing import Any, Literal, TextIO
 
 import cibuildwheel
-import cibuildwheel.ios
-import cibuildwheel.linux
-import cibuildwheel.macos
-import cibuildwheel.pyodide
 import cibuildwheel.util
-import cibuildwheel.windows
 from cibuildwheel import errors
 from cibuildwheel.architecture import Architecture, allowed_architectures_check
 from cibuildwheel.ci import CIProvider, detect_ci_provider, fix_ansi_codes_for_github_actions
 from cibuildwheel.logger import log
 from cibuildwheel.options import CommandLineArguments, Options, compute_options
-from cibuildwheel.selector import BuildSelector, EnableGroup
-from cibuildwheel.typing import PLATFORMS, GenericPythonConfiguration, PlatformName
+from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers
+from cibuildwheel.selector import BuildSelector, EnableGroup, selector_matches
+from cibuildwheel.typing import PLATFORMS, PlatformName
 from cibuildwheel.util.file import CIBW_CACHE_PATH
 from cibuildwheel.util.helpers import strtobool
 
@@ -286,30 +282,6 @@ def _compute_platform(args: CommandLineArguments) -> PlatformName:
     return _compute_platform_auto()
 
 
-class PlatformModule(Protocol):
-    # note that as per PEP544, the self argument is ignored when the protocol
-    # is applied to a module
-    def get_python_configurations(
-        self, build_selector: BuildSelector, architectures: Set[Architecture]
-    ) -> Sequence[GenericPythonConfiguration]: ...
-
-    def build(self, options: Options, tmp_path: Path) -> None: ...
-
-
-def get_platform_module(platform: PlatformName) -> PlatformModule:
-    if platform == "linux":
-        return cibuildwheel.linux
-    if platform == "windows":
-        return cibuildwheel.windows
-    if platform == "macos":
-        return cibuildwheel.macos
-    if platform == "pyodide":
-        return cibuildwheel.pyodide
-    if platform == "ios":
-        return cibuildwheel.ios
-    assert_never(platform)
-
-
 @contextlib.contextmanager
 def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
     """
@@ -362,7 +334,7 @@ def build_in_directory(args: CommandLineArguments) -> None:
         msg = f"Could not find any of {{{names}}} at root of package"
         raise errors.ConfigurationError(msg)
 
-    platform_module = get_platform_module(platform)
+    platform_module = ALL_PLATFORM_MODULES[platform]
     identifiers = get_build_identifiers(
         platform_module=platform_module,
         build_selector=options.globals.build_selector,
@@ -448,15 +420,6 @@ def print_preamble(platform: str, options: Options, identifiers: Sequence[str])
     print("Here we go!\n")
 
 
-def get_build_identifiers(
-    platform_module: PlatformModule,
-    build_selector: BuildSelector,
-    architectures: Set[Architecture],
-) -> list[str]:
-    python_configurations = platform_module.get_python_configurations(build_selector, architectures)
-    return [config.identifier for config in python_configurations]
-
-
 def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]:
     warnings = []
 
@@ -482,6 +445,75 @@ def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str
             )
             raise errors.ConfigurationError(msg)
 
+    build_selector = options.globals.build_selector
+    test_selector = options.globals.test_selector
+
+    all_valid_identifiers = [
+        config.identifier
+        for module in ALL_PLATFORM_MODULES.values()
+        for config in module.all_python_configurations()
+    ]
+
+    enabled_selector = BuildSelector(
+        build_config="*", skip_config="", enable=options.globals.build_selector.enable
+    )
+    all_enabled_identifiers = [
+        identifier for identifier in all_valid_identifiers if enabled_selector(identifier)
+    ]
+
+    warnings += check_for_invalid_selectors(
+        selector_name="build",
+        selector_value=build_selector.build_config,
+        all_valid_identifiers=all_valid_identifiers,
+        all_enabled_identifiers=all_enabled_identifiers,
+    )
+    warnings += check_for_invalid_selectors(
+        selector_name="skip",
+        selector_value=build_selector.skip_config,
+        all_valid_identifiers=all_valid_identifiers,
+        all_enabled_identifiers=all_enabled_identifiers,
+    )
+    warnings += check_for_invalid_selectors(
+        selector_name="test_skip",
+        selector_value=test_selector.skip_config,
+        all_valid_identifiers=all_valid_identifiers,
+        all_enabled_identifiers=all_enabled_identifiers,
+    )
+
+    return warnings
+
+
+def check_for_invalid_selectors(
+    *,
+    selector_name: Literal["build", "skip", "test_skip"],
+    selector_value: str,
+    all_valid_identifiers: Sequence[str],
+    all_enabled_identifiers: Sequence[str],
+) -> list[str]:
+    warnings = []
+
+    for selector in selector_value.split():
+        if not any(selector_matches(selector, i) for i in all_enabled_identifiers):
+            msg = f"Invalid {selector_name} selector: {selector!r}. "
+            error_type: type = errors.ConfigurationError
+
+            if any(selector_matches(selector, i) for i in all_valid_identifiers):
+                msg += "This selector matches a group that wasn't enabled. Enable it using the `enable` option or remove this selector. "
+
+            if "p2" in selector or "p35" in selector:
+                msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update `{selector_name}`. "
+                error_type = errors.DeprecationError
+            if "p36" in selector or "p37" in selector:
+                msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update `{selector_name}`. "
+                error_type = errors.DeprecationError
+
+            if selector_name == "build":
+                raise error_type(msg)
+
+            msg += "This selector will have no effect. "
+
+            warnings.append(msg)
+
     return warnings
 
 
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 5ab256232..482035a8e 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -863,14 +863,6 @@ def check_for_invalid_configuration(self, identifiers: Iterable[str]) -> None:
                     )
                 )
 
-    def check_for_deprecated_options(self) -> None:
-        build_selector = self.globals.build_selector
-        test_selector = self.globals.test_selector
-
-        deprecated_selectors("CIBW_BUILD", build_selector.build_config, error=True)
-        deprecated_selectors("CIBW_SKIP", build_selector.skip_config)
-        deprecated_selectors("CIBW_TEST_SKIP", test_selector.skip_config)
-
     @functools.cached_property
     def defaults(self) -> Self:
         return self.__class__(
@@ -985,9 +977,7 @@ def compute_options(
     command_line_arguments: CommandLineArguments,
     env: Mapping[str, str],
 ) -> Options:
-    options = Options(platform=platform, command_line_arguments=command_line_arguments, env=env)
-    options.check_for_deprecated_options()
-    return options
+    return Options(platform=platform, command_line_arguments=command_line_arguments, env=env)
 
 
 @functools.cache
@@ -1002,16 +992,3 @@ def _get_pinned_container_images() -> Mapping[str, Mapping[str, str]]:
     all_pinned_images = configparser.ConfigParser()
     all_pinned_images.read(resources.PINNED_DOCKER_IMAGES)
     return all_pinned_images
-
-
-def deprecated_selectors(name: str, selector: str, *, error: bool = False) -> None:
-    if "p2" in selector or "p35" in selector:
-        msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update {name}"
-        if error:
-            raise errors.DeprecationError(msg)
-        log.warning(msg)
-    if "p36" in selector or "p37" in selector:
-        msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update {name}"
-        if error:
-            raise errors.DeprecationError(msg)
-        log.warning(msg)
diff --git a/cibuildwheel/platforms/__init__.py b/cibuildwheel/platforms/__init__.py
new file mode 100644
index 000000000..a56fb9036
--- /dev/null
+++ b/cibuildwheel/platforms/__init__.py
@@ -0,0 +1,41 @@
+from __future__ import annotations
+
+from collections.abc import Sequence
+from pathlib import Path
+from typing import Final, Protocol
+
+from cibuildwheel.architecture import Architecture
+from cibuildwheel.options import Options
+from cibuildwheel.platforms import ios, linux, macos, pyodide, windows
+from cibuildwheel.selector import BuildSelector
+from cibuildwheel.typing import GenericPythonConfiguration, PlatformName
+
+
+class PlatformModule(Protocol):
+    # note that as per PEP544, the self argument is ignored when the protocol
+    # is applied to a module
+    def all_python_configurations(self) -> Sequence[GenericPythonConfiguration]: ...
+
+    def get_python_configurations(
+        self, build_selector: BuildSelector, architectures: set[Architecture]
+    ) -> Sequence[GenericPythonConfiguration]: ...
+
+    def build(self, options: Options, tmp_path: Path) -> None: ...
+
+
+ALL_PLATFORM_MODULES: Final[dict[PlatformName, PlatformModule]] = {
+    "linux": linux,
+    "windows": windows,
+    "macos": macos,
+    "pyodide": pyodide,
+    "ios": ios,
+}
+
+
+def get_build_identifiers(
+    platform_module: PlatformModule,
+    build_selector: BuildSelector,
+    architectures: set[Architecture],
+) -> list[str]:
+    python_configurations = platform_module.get_python_configurations(build_selector, architectures)
+    return [config.identifier for config in python_configurations]
diff --git a/cibuildwheel/ios.py b/cibuildwheel/platforms/ios.py
similarity index 97%
rename from cibuildwheel/ios.py
rename to cibuildwheel/platforms/ios.py
index 52411e31a..25dc2a461 100644
--- a/cibuildwheel/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -12,34 +12,34 @@
 
 from filelock import FileLock
 
-from . import errors
-from .architecture import Architecture
-from .environment import ParsedEnvironment
-from .frontend import (
+from .. import errors
+from ..architecture import Architecture
+from ..environment import ParsedEnvironment
+from ..frontend import (
     BuildFrontendConfig,
     BuildFrontendName,
     get_build_frontend_extra_flags,
 )
-from .logger import log
-from .macos import install_cpython as install_build_cpython
-from .options import Options
-from .selector import BuildSelector
-from .typing import PathOrStr
-from .util import resources
-from .util.cmd import call, shell
-from .util.file import (
+from ..logger import log
+from ..options import Options
+from ..selector import BuildSelector
+from ..typing import PathOrStr
+from ..util import resources
+from ..util.cmd import call, shell
+from ..util.file import (
     CIBW_CACHE_PATH,
     copy_test_sources,
     download,
     move_file,
 )
-from .util.helpers import prepare_command
-from .util.packaging import (
+from ..util.helpers import prepare_command
+from ..util.packaging import (
     combine_constraints,
     find_compatible_wheel,
     get_pip_version,
 )
-from .venv import virtualenv
+from ..venv import virtualenv
+from .macos import install_cpython as install_build_cpython
 
 
 @dataclass(frozen=True)
@@ -72,10 +72,7 @@ def xcframework_slice(self) -> str:
         return "ios-arm64_x86_64-simulator" if self.is_simulator else "ios-arm64"
 
 
-def get_python_configurations(
-    build_selector: BuildSelector,
-    architectures: Set[Architecture],
-) -> list[PythonConfiguration]:
+def all_python_configurations() -> list[PythonConfiguration]:
     # iOS builds are always cross builds; we need to install a macOS Python as
     # well. Rather than duplicate the location of the URL of macOS installers,
     # load the macos configurations, determine the macOS configuration that
@@ -97,7 +94,7 @@ def build_url(config_dict: dict[str, str]) -> str:
     # Load the platform configuration
     full_python_configs = resources.read_python_configs("ios")
     # Build the configurations, annotating with macOS URL details.
-    python_configurations = [
+    return [
         PythonConfiguration(
             **item,
             build_url=build_url(item),
@@ -105,6 +102,13 @@ def build_url(config_dict: dict[str, str]) -> str:
         for item in full_python_configs
     ]
 
+
+def get_python_configurations(
+    build_selector: BuildSelector,
+    architectures: Set[Architecture],
+) -> list[PythonConfiguration]:
+    python_configurations = all_python_configurations()
+
     # Filter out configs that don't match any of the selected architectures
     python_configurations = [
         c
diff --git a/cibuildwheel/linux.py b/cibuildwheel/platforms/linux.py
similarity index 96%
rename from cibuildwheel/linux.py
rename to cibuildwheel/platforms/linux.py
index 7f0a6e77d..4932298aa 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -8,18 +8,18 @@
 from pathlib import Path, PurePath, PurePosixPath
 from typing import assert_never
 
-from . import errors
-from .architecture import Architecture
-from .frontend import BuildFrontendConfig, get_build_frontend_extra_flags
-from .logger import log
-from .oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform
-from .options import BuildOptions, Options
-from .selector import BuildSelector
-from .typing import PathOrStr
-from .util import resources
-from .util.file import copy_test_sources
-from .util.helpers import prepare_command, unwrap
-from .util.packaging import find_compatible_wheel
+from .. import errors
+from ..architecture import Architecture
+from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags
+from ..logger import log
+from ..oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform
+from ..options import BuildOptions, Options
+from ..selector import BuildSelector
+from ..typing import PathOrStr
+from ..util import resources
+from ..util.file import copy_test_sources
+from ..util.helpers import prepare_command, unwrap
+from ..util.packaging import find_compatible_wheel
 
 ARCHITECTURE_OCI_PLATFORM_MAP = {
     Architecture.x86_64: OCIPlatform.AMD64,
@@ -50,13 +50,16 @@ class BuildStep:
     container_image: str
 
 
+def all_python_configurations() -> list[PythonConfiguration]:
+    config_dicts = resources.read_python_configs("linux")
+    return [PythonConfiguration(**item) for item in config_dicts]
+
+
 def get_python_configurations(
     build_selector: BuildSelector,
     architectures: Set[Architecture],
 ) -> list[PythonConfiguration]:
-    full_python_configs = resources.read_python_configs("linux")
-
-    python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
+    python_configurations = all_python_configurations()
 
     # return all configurations whose arch is in our `architectures` set,
     # and match the build/skip rules
diff --git a/cibuildwheel/macos.py b/cibuildwheel/platforms/macos.py
similarity index 97%
rename from cibuildwheel/macos.py
rename to cibuildwheel/platforms/macos.py
index b27348d7a..4c75859f0 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -15,26 +15,26 @@
 from filelock import FileLock
 from packaging.version import Version
 
-from . import errors
-from .architecture import Architecture
-from .ci import detect_ci_provider
-from .environment import ParsedEnvironment
-from .frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags
-from .logger import log
-from .options import Options
-from .selector import BuildSelector
-from .typing import PathOrStr
-from .util import resources
-from .util.cmd import call, shell
-from .util.file import (
+from .. import errors
+from ..architecture import Architecture
+from ..ci import detect_ci_provider
+from ..environment import ParsedEnvironment
+from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags
+from ..logger import log
+from ..options import Options
+from ..selector import BuildSelector
+from ..typing import PathOrStr
+from ..util import resources
+from ..util.cmd import call, shell
+from ..util.file import (
     CIBW_CACHE_PATH,
     copy_test_sources,
     download,
     move_file,
 )
-from .util.helpers import prepare_command, unwrap
-from .util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
-from .venv import find_uv, virtualenv
+from ..util.helpers import prepare_command, unwrap
+from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
+from ..venv import find_uv, virtualenv
 
 
 @functools.cache
@@ -84,12 +84,15 @@ class PythonConfiguration:
     url: str
 
 
+def all_python_configurations() -> list[PythonConfiguration]:
+    config_dicts = resources.read_python_configs("macos")
+    return [PythonConfiguration(**item) for item in config_dicts]
+
+
 def get_python_configurations(
     build_selector: BuildSelector, architectures: Set[Architecture]
 ) -> list[PythonConfiguration]:
-    full_python_configs = resources.read_python_configs("macos")
-
-    python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
+    python_configurations = all_python_configurations()
 
     # filter out configs that don't match any of the selected architectures
     python_configurations = [
diff --git a/cibuildwheel/pyodide.py b/cibuildwheel/platforms/pyodide.py
similarity index 95%
rename from cibuildwheel/pyodide.py
rename to cibuildwheel/platforms/pyodide.py
index 3c56df7e7..ca9edcb20 100644
--- a/cibuildwheel/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -11,17 +11,17 @@
 
 from filelock import FileLock
 
-from . import errors
-from .architecture import Architecture
-from .environment import ParsedEnvironment
-from .frontend import BuildFrontendConfig, get_build_frontend_extra_flags
-from .logger import log
-from .options import Options
-from .selector import BuildSelector
-from .typing import PathOrStr
-from .util import resources
-from .util.cmd import call, shell
-from .util.file import (
+from .. import errors
+from ..architecture import Architecture
+from ..environment import ParsedEnvironment
+from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags
+from ..logger import log
+from ..options import Options
+from ..selector import BuildSelector
+from ..typing import PathOrStr
+from ..util import resources
+from ..util.cmd import call, shell
+from ..util.file import (
     CIBW_CACHE_PATH,
     copy_test_sources,
     download,
@@ -29,9 +29,9 @@
     extract_zip,
     move_file,
 )
-from .util.helpers import prepare_command
-from .util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
-from .venv import virtualenv
+from ..util.helpers import prepare_command
+from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
+from ..venv import virtualenv
 
 IS_WIN: Final[bool] = sys.platform.startswith("win")
 
@@ -215,15 +215,16 @@ def setup_python(
     return env
 
 
+def all_python_configurations() -> list[PythonConfiguration]:
+    full_python_configs = resources.read_python_configs("pyodide")
+    return [PythonConfiguration(**item) for item in full_python_configs]
+
+
 def get_python_configurations(
     build_selector: BuildSelector,
     architectures: Set[Architecture],  # noqa: ARG001
 ) -> list[PythonConfiguration]:
-    full_python_configs = resources.read_python_configs("pyodide")
-
-    python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
-    python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
-    return python_configurations
+    return [c for c in all_python_configurations() if build_selector(c.identifier)]
 
 
 def build(options: Options, tmp_path: Path) -> None:
diff --git a/cibuildwheel/windows.py b/cibuildwheel/platforms/windows.py
similarity index 96%
rename from cibuildwheel/windows.py
rename to cibuildwheel/platforms/windows.py
index 374b68d71..6d56236a2 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -11,20 +11,20 @@
 
 from filelock import FileLock
 
-from . import errors
-from .architecture import Architecture
-from .environment import ParsedEnvironment
-from .frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags
-from .logger import log
-from .options import Options
-from .selector import BuildSelector
-from .typing import PathOrStr
-from .util import resources
-from .util.cmd import call, shell
-from .util.file import CIBW_CACHE_PATH, copy_test_sources, download, extract_zip, move_file
-from .util.helpers import prepare_command, unwrap
-from .util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
-from .venv import find_uv, virtualenv
+from .. import errors
+from ..architecture import Architecture
+from ..environment import ParsedEnvironment
+from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags
+from ..logger import log
+from ..options import Options
+from ..selector import BuildSelector
+from ..typing import PathOrStr
+from ..util import resources
+from ..util.cmd import call, shell
+from ..util.file import CIBW_CACHE_PATH, copy_test_sources, download, extract_zip, move_file
+from ..util.helpers import prepare_command, unwrap
+from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
+from ..venv import find_uv, virtualenv
 
 
 def get_nuget_args(
@@ -59,13 +59,16 @@ class PythonConfiguration:
     url: str | None = None
 
 
+def all_python_configurations() -> list[PythonConfiguration]:
+    config_dicts = resources.read_python_configs("windows")
+    return [PythonConfiguration(**item) for item in config_dicts]
+
+
 def get_python_configurations(
     build_selector: BuildSelector,
     architectures: Set[Architecture],
 ) -> list[PythonConfiguration]:
-    full_python_configs = resources.read_python_configs("windows")
-
-    python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
+    python_configurations = all_python_configurations()
 
     map_arch = {"32": Architecture.x86, "64": Architecture.AMD64, "ARM64": Architecture.ARM64}
 
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index bd0ce597c..0e9736f53 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -1,6 +1,6 @@
 import itertools
 from dataclasses import dataclass
-from enum import Enum
+from enum import StrEnum
 from fnmatch import fnmatch
 from typing import Any
 
@@ -24,7 +24,7 @@ def selector_matches(patterns: str, string: str) -> bool:
     return any(fnmatch(string, pat) for pat in expanded_patterns)
 
 
-class EnableGroup(Enum):
+class EnableGroup(StrEnum):
     """
     Groups of build selectors that are not enabled by default.
     """
@@ -33,6 +33,10 @@ class EnableGroup(Enum):
     CPythonPrerelease = "cpython-prerelease"
     PyPy = "pypy"
 
+    @classmethod
+    def all_groups(cls) -> frozenset["EnableGroup"]:
+        return frozenset(cls)
+
 
 @dataclass(frozen=True, kw_only=True)
 class BuildSelector:
diff --git a/test/test_0_basic.py b/test/test_0_basic.py
index 317e876d4..4ef40a2c2 100644
--- a/test/test_0_basic.py
+++ b/test/test_0_basic.py
@@ -83,7 +83,7 @@ def test_allow_empty(tmp_path, add_args, env_allow_empty):
     # without error
     actual_wheels = utils.cibuildwheel_run(
         project_dir,
-        add_env={"CIBW_BUILD": "BUILD_NOTHING_AT_ALL", **env_allow_empty},
+        add_env={"CIBW_SKIP": "*", **env_allow_empty},
         add_args=add_args,
     )
 
diff --git a/test/utils.py b/test/utils.py
index 4548f0a52..4ea3dd4c5 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -17,6 +17,7 @@
 
 from cibuildwheel.architecture import Architecture
 from cibuildwheel.ci import CIProvider, detect_ci_provider
+from cibuildwheel.selector import EnableGroup
 from cibuildwheel.util.file import CIBW_CACHE_PATH
 
 EMULATED_ARCHS: Final[list[str]] = sorted(
@@ -120,7 +121,7 @@ def cibuildwheel_run(
 
     _update_pip_cache_dir(env)
 
-    env["CIBW_ENABLE"] = "cpython-prerelease cpython-freethreading pypy"
+    env["CIBW_ENABLE"] = " ".join(EnableGroup.all_groups())
 
     if single_python:
         env["CIBW_BUILD"] = "cp{}{}-*".format(*SINGLE_PYTHON_VERSION)
diff --git a/unit_test/get_platform_test.py b/unit_test/get_platform_test.py
index 6f691bf6f..9bd46cd99 100644
--- a/unit_test/get_platform_test.py
+++ b/unit_test/get_platform_test.py
@@ -7,7 +7,7 @@
 
 from cibuildwheel.ci import CIProvider, detect_ci_provider
 from cibuildwheel.errors import FatalError
-from cibuildwheel.windows import PythonConfiguration, setup_setuptools_cross_compile
+from cibuildwheel.platforms.windows import PythonConfiguration, setup_setuptools_cross_compile
 
 # monkeypatching os.name is too flaky. E.g. It works on my machine, but fails in pipeline
 if not sys.platform.startswith("win"):
diff --git a/unit_test/linux_build_steps_test.py b/unit_test/linux_build_steps_test.py
index b493496ef..cd295a5e9 100644
--- a/unit_test/linux_build_steps_test.py
+++ b/unit_test/linux_build_steps_test.py
@@ -4,7 +4,7 @@
 
 import pytest
 
-import cibuildwheel.linux
+import cibuildwheel.platforms.linux
 from cibuildwheel.oci_container import OCIContainerEngineConfig
 from cibuildwheel.options import CommandLineArguments, Options
 
@@ -46,20 +46,22 @@ def test_linux_container_split(tmp_path: Path, monkeypatch: pytest.MonkeyPatch)
     monkeypatch.chdir(tmp_path)
     options = Options("linux", command_line_arguments=args, env={})
 
-    python_configurations = cibuildwheel.linux.get_python_configurations(
+    python_configurations = cibuildwheel.platforms.linux.get_python_configurations(
         options.globals.build_selector, options.globals.architectures
     )
 
-    build_steps = list(cibuildwheel.linux.get_build_steps(options, python_configurations))
+    build_steps = list(cibuildwheel.platforms.linux.get_build_steps(options, python_configurations))
 
     # helper functions to extract test info
-    def identifiers(step: cibuildwheel.linux.BuildStep) -> list[str]:
+    def identifiers(step: cibuildwheel.platforms.linux.BuildStep) -> list[str]:
         return [c.identifier for c in step.platform_configs]
 
-    def before_alls(step: cibuildwheel.linux.BuildStep) -> list[str]:
+    def before_alls(step: cibuildwheel.platforms.linux.BuildStep) -> list[str]:
         return [options.build_options(c.identifier).before_all for c in step.platform_configs]
 
-    def container_engines(step: cibuildwheel.linux.BuildStep) -> list[OCIContainerEngineConfig]:
+    def container_engines(
+        step: cibuildwheel.platforms.linux.BuildStep,
+    ) -> list[OCIContainerEngineConfig]:
         return [options.build_options(c.identifier).container_engine for c in step.platform_configs]
 
     pprint(build_steps)
diff --git a/unit_test/main_tests/conftest.py b/unit_test/main_tests/conftest.py
index 3751b78a5..e01c8c027 100644
--- a/unit_test/main_tests/conftest.py
+++ b/unit_test/main_tests/conftest.py
@@ -6,7 +6,8 @@
 
 import pytest
 
-from cibuildwheel import __main__, architecture, linux, macos, pyodide, windows
+from cibuildwheel import __main__, architecture
+from cibuildwheel.platforms import linux, macos, pyodide, windows
 from cibuildwheel.util import file
 
 
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index a869d615f..efc1074cc 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -50,22 +50,49 @@ def test_output_dir_argument(also_set_environment, intercepted_build_args, monke
 
 @pytest.mark.usefixtures("platform", "allow_empty")
 def test_build_selector(intercepted_build_args, monkeypatch):
-    BUILD = "some build* *-selector"
-    SKIP = "some skip* *-selector"
-
-    monkeypatch.setenv("CIBW_BUILD", BUILD)
-    monkeypatch.setenv("CIBW_SKIP", SKIP)
+    monkeypatch.setenv("CIBW_BUILD", "cp313-*")
+    monkeypatch.setenv("CIBW_SKIP", "cp39-*")
 
     main()
 
     intercepted_build_selector = intercepted_build_args.args[0].globals.build_selector
     assert isinstance(intercepted_build_selector, BuildSelector)
-    assert intercepted_build_selector("build24-this")
-    assert not intercepted_build_selector("skip65-that")
+    assert intercepted_build_selector("cp313-something-to-build")
+    assert not intercepted_build_selector("cp39-something-to-skip")
     # This unit test is just testing the options of 'main'
     # Unit tests for BuildSelector are in build_selector_test.py
 
 
+@pytest.mark.usefixtures("platform", "allow_empty")
+def test_invalid_build_selector(monkeypatch, capsys):
+    monkeypatch.setenv("CIBW_BUILD", "invalid")
+
+    with pytest.raises(SystemExit) as e:
+        main()
+
+    assert e.value.code == 2
+    _, err = capsys.readouterr()
+    assert "Invalid build selector" in err
+
+
+@pytest.mark.parametrize(
+    ("option_name", "option_env_var"),
+    [
+        ("skip", "CIBW_SKIP"),
+        ("test_skip", "CIBW_TEST_SKIP"),
+    ],
+)
+@pytest.mark.usefixtures("platform", "intercepted_build_args")
+def test_invalid_skip_selector(monkeypatch, capsys, option_name, option_env_var):
+    monkeypatch.setenv(option_env_var, "invalid")
+
+    main()
+
+    _, err = capsys.readouterr()
+    print(err)
+    assert f"Invalid {option_name} selector" in err
+
+
 @pytest.mark.usefixtures("platform", "intercepted_build_args")
 def test_empty_selector(monkeypatch):
     monkeypatch.setenv("CIBW_SKIP", "*")
@@ -313,7 +340,7 @@ def test_build_selector_deprecated_error(monkeypatch, selector, pattern, capsys)
 
     stderr = capsys.readouterr().err
     series = "2" if "6" in pattern else "1"
-    msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the {series}.x series or update {selector}"
+    msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the {series}.x series or update"
     assert msg in stderr
 
 
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 27b25377c..f17841fbe 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -8,7 +8,7 @@
 
 import pytest
 
-from cibuildwheel import linux
+from cibuildwheel import platforms
 from cibuildwheel.__main__ import main
 from cibuildwheel.oci_container import OCIPlatform
 from cibuildwheel.util import file
@@ -39,10 +39,11 @@ def ignore_context_call(*args, **kwargs):
     monkeypatch.setattr(subprocess, "Popen", fail_on_call)
     monkeypatch.setattr(subprocess, "run", ignore_call)
     monkeypatch.setattr(file, "download", fail_on_call)
-    monkeypatch.setattr("cibuildwheel.linux.OCIContainer", ignore_context_call)
+    monkeypatch.setattr("cibuildwheel.platforms.linux.OCIContainer", ignore_context_call)
 
     monkeypatch.setattr(
-        "cibuildwheel.linux.build_in_container", mock.Mock(spec=linux.build_in_container)
+        "cibuildwheel.platforms.linux.build_in_container",
+        mock.Mock(spec=platforms.linux.build_in_container),
     )
     monkeypatch.setattr("cibuildwheel.__main__.print_new_wheels", ignore_context_call)
 
@@ -53,7 +54,7 @@ def test_build_default_launches(monkeypatch):
 
     main()
 
-    build_in_container = typing.cast(mock.Mock, linux.build_in_container)
+    build_in_container = typing.cast(mock.Mock, platforms.linux.build_in_container)
 
     assert build_in_container.call_count == 4
 
@@ -121,7 +122,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 
     main()
 
-    build_in_container = typing.cast(mock.Mock, linux.build_in_container)
+    build_in_container = typing.cast(mock.Mock, platforms.linux.build_in_container)
 
     assert build_in_container.call_count == 6
 
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index 7f497a375..143f7fdc0 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -6,13 +6,13 @@
 import pytest
 
 from cibuildwheel import errors
-from cibuildwheel.__main__ import get_build_identifiers, get_platform_module
 from cibuildwheel.bashlex_eval import local_environment_executor
 from cibuildwheel.options import (
     CommandLineArguments,
     Options,
     _get_pinned_container_images,
 )
+from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers
 from cibuildwheel.selector import EnableGroup
 from cibuildwheel.util import resources
 from cibuildwheel.util.packaging import DependencyConstraints
@@ -51,7 +51,7 @@ def test_options_1(tmp_path, monkeypatch):
 
     options = Options(platform="linux", command_line_arguments=args, env={})
 
-    module = get_platform_module("linux")
+    module = ALL_PLATFORM_MODULES["linux"]
     identifiers = get_build_identifiers(
         platform_module=module,
         build_selector=options.globals.build_selector,

From e6eff86c985b63a71d5c1867eda3e50bc36e3a47 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 24 Mar 2025 18:00:44 +0100
Subject: [PATCH 025/193] feat: move default manylinux build to manylinux_2_28
 (#2330)

---
 cibuildwheel/options.py                   |  13 +--
 cibuildwheel/resources/defaults.toml      |  12 +--
 docs/options.md                           |  36 +++----
 test/test_container_images.py             |   5 +-
 test/utils.py                             | 126 +++++++++++-----------
 unit_test/main_tests/main_options_test.py |   4 +-
 unit_test/option_prepare_test.py          |   2 +-
 unit_test/options_toml_test.py            |   2 +-
 8 files changed, 100 insertions(+), 100 deletions(-)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 482035a8e..78cbaf519 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -795,10 +795,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                         f"manylinux-{build_platform}-image", ignore_empty=True
                     )
                     self._check_pinned_image(config_value, pinned_images)
-                    if not config_value:
-                        # default to manylinux2014
-                        image = pinned_images["manylinux2014"]
-                    elif config_value in pinned_images:
+                    if config_value in pinned_images:
                         image = pinned_images[config_value]
                     else:
                         image = config_value
@@ -806,11 +803,11 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
 
                 for build_platform in MUSLLINUX_ARCHS:
                     pinned_images = all_pinned_container_images[build_platform]
-                    config_value = self.reader.get(f"musllinux-{build_platform}-image")
+                    config_value = self.reader.get(
+                        f"musllinux-{build_platform}-image", ignore_empty=True
+                    )
                     self._check_pinned_image(config_value, pinned_images)
-                    if not config_value:
-                        image = pinned_images["musllinux_1_2"]
-                    elif config_value in pinned_images:
+                    if config_value in pinned_images:
                         image = pinned_images[config_value]
                     else:
                         image = config_value
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 6b65e6369..bd17245b4 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -25,15 +25,15 @@ test-groups = []
 
 container-engine = "docker"
 
-manylinux-x86_64-image = "manylinux2014"
+manylinux-x86_64-image = "manylinux_2_28"
 manylinux-i686-image = "manylinux2014"
-manylinux-aarch64-image = "manylinux2014"
-manylinux-ppc64le-image = "manylinux2014"
-manylinux-s390x-image = "manylinux2014"
+manylinux-aarch64-image = "manylinux_2_28"
+manylinux-ppc64le-image = "manylinux_2_28"
+manylinux-s390x-image = "manylinux_2_28"
 manylinux-armv7l-image = "manylinux_2_31"
-manylinux-pypy_x86_64-image = "manylinux2014"
+manylinux-pypy_x86_64-image = "manylinux_2_28"
 manylinux-pypy_i686-image = "manylinux2014"
-manylinux-pypy_aarch64-image = "manylinux2014"
+manylinux-pypy_aarch64-image = "manylinux_2_28"
 
 musllinux-x86_64-image = "musllinux_1_2"
 musllinux-i686-image = "musllinux_1_2"
diff --git a/docs/options.md b/docs/options.md
index 2e8a199c7..0c4ade208 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1168,25 +1168,23 @@ Platform-specific environment variables are also available:<br/>
 
 The available options are:
 
-| Option                            | Default                                                        | Future default*                                                 |
-|-----------------------------------|----------------------------------------------------------------|-----------------------------------------------------------------|
-| CIBW_MANYLINUX_X86_64_IMAGE       | [`manylinux2014`](https://quay.io/pypa/manylinux2014_x86_64)   | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
-| CIBW_MANYLINUX_I686_IMAGE         | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)     |                                                                 |
-| CIBW_MANYLINUX_PYPY_X86_64_IMAGE  | [`manylinux2014`](https://quay.io/pypa/manylinux2014_x86_64)   | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
-| CIBW_MANYLINUX_AARCH64_IMAGE      | [`manylinux2014`](https://quay.io/pypa/manylinux2014_aarch64)  | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
-| CIBW_MANYLINUX_PPC64LE_IMAGE      | [`manylinux2014`](https://quay.io/pypa/manylinux2014_ppc64le)  | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
-| CIBW_MANYLINUX_S390X_IMAGE        | [`manylinux2014`](https://quay.io/pypa/manylinux2014_s390x)    | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x)   |
-| CIBW_MANYLINUX_ARMV7L_IMAGE       | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l) |                                                                 |
-| CIBW_MANYLINUX_PYPY_AARCH64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_aarch64)  | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
-| CIBW_MANYLINUX_PYPY_I686_IMAGE    | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)     |                                                                 |
-| CIBW_MUSLLINUX_X86_64_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64)   |                                                                 |
-| CIBW_MUSLLINUX_I686_IMAGE         | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686)     |                                                                 |
-| CIBW_MUSLLINUX_AARCH64_IMAGE      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64)  |                                                                 |
-| CIBW_MUSLLINUX_PPC64LE_IMAGE      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le)  |                                                                 |
-| CIBW_MUSLLINUX_S390X_IMAGE        | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x)    |                                                                 |
-| CIBW_MUSLLINUX_ARMV7L_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l)   |                                                                 |
-
-<small>* The default is scheduled to change in a cibuildwheel release on or after 6th May 2025 - if you don't want the new default, you should set the value to `manylinux2014`.</small>
+| Option                            | Default                                                         |
+|-----------------------------------|-----------------------------------------------------------------|
+| CIBW_MANYLINUX_X86_64_IMAGE       | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
+| CIBW_MANYLINUX_I686_IMAGE         | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
+| CIBW_MANYLINUX_PYPY_X86_64_IMAGE  | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
+| CIBW_MANYLINUX_AARCH64_IMAGE      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
+| CIBW_MANYLINUX_PPC64LE_IMAGE      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
+| CIBW_MANYLINUX_S390X_IMAGE        | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x)   |
+| CIBW_MANYLINUX_ARMV7L_IMAGE       | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l)  |
+| CIBW_MANYLINUX_PYPY_AARCH64_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
+| CIBW_MANYLINUX_PYPY_I686_IMAGE    | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
+| CIBW_MUSLLINUX_X86_64_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64)    |
+| CIBW_MUSLLINUX_I686_IMAGE         | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686)      |
+| CIBW_MUSLLINUX_AARCH64_IMAGE      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64)   |
+| CIBW_MUSLLINUX_PPC64LE_IMAGE      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le)   |
+| CIBW_MUSLLINUX_S390X_IMAGE        | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x)     |
+| CIBW_MUSLLINUX_ARMV7L_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l)    |
 
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
 
diff --git a/test/test_container_images.py b/test/test_container_images.py
index 17626a2ef..f1d4fda8b 100644
--- a/test/test_container_images.py
+++ b/test/test_container_images.py
@@ -43,9 +43,12 @@ def test(tmp_path):
     )
 
     # also check that we got the right wheels built
+    manylinux_versions = ["manylinux_2_5", "manylinux1", "manylinux_2_17", "manylinux2014"]
     expected_wheels = [
         w
-        for w in utils.expected_wheels("spam", "0.1.0", musllinux_versions=[])
+        for w in utils.expected_wheels(
+            "spam", "0.1.0", manylinux_versions=manylinux_versions, musllinux_versions=[]
+        )
         if "-cp38-" in w or "-cp39-" in w
     ]
     assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/utils.py b/test/utils.py
index 4ea3dd4c5..02659fb6e 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -163,6 +163,57 @@ def expected_wheels(
     include_universal2: bool = False,
     single_python: bool = False,
     single_arch: bool = False,
+) -> list[str]:
+    """
+    Returns a list of expected wheels from a run of cibuildwheel.
+    """
+    if machine_arch is None:
+        machine_arch = pm.machine()
+        if platform == "linux":
+            machine_arch = arch_name_for_linux(machine_arch)
+
+    architectures = [machine_arch]
+    if not single_arch:
+        if platform == "linux":
+            if machine_arch == "x86_64":
+                architectures.append("i686")
+            elif (
+                machine_arch == "aarch64"
+                and sys.platform.startswith("linux")
+                and _AARCH64_CAN_RUN_ARMV7
+            ):
+                architectures.append("armv7l")
+        elif platform == "windows" and machine_arch == "AMD64":
+            architectures.append("x86")
+
+    wheels: list[str] = []
+    for architecture in architectures:
+        wheels.extend(
+            _expected_wheels(
+                package_name,
+                package_version,
+                architecture,
+                manylinux_versions,
+                musllinux_versions,
+                macosx_deployment_target,
+                python_abi_tags,
+                include_universal2,
+                single_python,
+            )
+        )
+    return wheels
+
+
+def _expected_wheels(
+    package_name: str,
+    package_version: str,
+    machine_arch: str,
+    manylinux_versions: list[str] | None,
+    musllinux_versions: list[str] | None,
+    macosx_deployment_target: str,
+    python_abi_tags: list[str] | None,
+    include_universal2: bool,
+    single_python: bool,
 ) -> list[str]:
     """
     Returns a list of expected wheels from a run of cibuildwheel.
@@ -172,24 +223,12 @@ def expected_wheels(
     # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
     # so we'll merge them below as python_abi_tag
 
-    if machine_arch is None:
-        machine_arch = pm.machine()
-        if platform == "linux" and machine_arch.lower() == "arm64":
-            # we're running linux tests from macOS/Windows arm64, override platform
-            machine_arch = "aarch64"
-
     if manylinux_versions is None:
-        if machine_arch in ("armv7l", "aarch64"):
-            manylinux_versions = ["manylinux_2_17", "manylinux2014", "manylinux_2_31"]
-        elif machine_arch == "x86_64":
-            manylinux_versions = [
-                "manylinux_2_5",
-                "manylinux1",
-                "manylinux_2_17",
-                "manylinux2014",
-            ]
-        else:
-            manylinux_versions = ["manylinux_2_17", "manylinux2014"]
+        manylinux_versions = {
+            "armv7l": ["manylinux_2_17", "manylinux2014", "manylinux_2_31"],
+            "i686": ["manylinux_2_5", "manylinux1", "manylinux_2_17", "manylinux2014"],
+            "x86_64": ["manylinux_2_5", "manylinux1", "manylinux_2_28"],
+        }.get(machine_arch, ["manylinux_2_17", "manylinux2014", "manylinux_2_28"])
 
     if musllinux_versions is None:
         musllinux_versions = ["musllinux_1_2"]
@@ -207,7 +246,7 @@ def expected_wheels(
             "cp313-cp313t",
         ]
 
-        if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
+        if machine_arch in ["x86_64", "i686", "AMD64", "aarch64", "arm64"]:
             python_abi_tags += [
                 "pp38-pypy38_pp73",
                 "pp39-pypy39_pp73",
@@ -215,22 +254,6 @@ def expected_wheels(
                 "pp311-pypy311_pp73",
             ]
 
-        if platform == "macos" and machine_arch == "arm64":
-            # arm64 macs are only supported by cp38+
-            python_abi_tags = [
-                "cp38-cp38",
-                "cp39-cp39",
-                "cp310-cp310",
-                "cp311-cp311",
-                "cp312-cp312",
-                "cp313-cp313",
-                "cp313-cp313t",
-                "pp38-pypy38_pp73",
-                "pp39-pypy39_pp73",
-                "pp310-pypy310_pp73",
-                "pp311-pypy311_pp73",
-            ]
-
     if single_python:
         python_tag = "cp{}{}-".format(*SINGLE_PYTHON_VERSION)
         python_abi_tags = [
@@ -253,44 +276,23 @@ def expected_wheels(
         platform_tags = []
 
         if platform == "linux":
-            architectures = [arch_name_for_linux(machine_arch)]
-
-            if not single_arch:
-                if machine_arch == "x86_64":
-                    architectures.append("i686")
-                elif (
-                    machine_arch == "aarch64"
-                    and sys.platform.startswith("linux")
-                    and not python_abi_tag.startswith("pp")
-                    and _AARCH64_CAN_RUN_ARMV7
-                ):
-                    architectures.append("armv7l")
-
             if len(manylinux_versions) > 0:
                 platform_tags = [
                     ".".join(
-                        f"{manylinux_version}_{architecture}"
+                        f"{manylinux_version}_{machine_arch}"
                         for manylinux_version in manylinux_versions
-                        if (manylinux_version, architecture) != ("manylinux_2_31", "aarch64")
                     )
-                    for architecture in architectures
                 ]
             if len(musllinux_versions) > 0 and not python_abi_tag.startswith("pp"):
-                platform_tags.extend(
-                    [
-                        ".".join(
-                            f"{musllinux_version}_{architecture}"
-                            for musllinux_version in musllinux_versions
-                        )
-                        for architecture in architectures
-                    ]
+                platform_tags.append(
+                    ".".join(
+                        f"{musllinux_version}_{machine_arch}"
+                        for musllinux_version in musllinux_versions
+                    )
                 )
 
         elif platform == "windows":
-            if python_abi_tag.startswith("pp"):
-                platform_tags = ["win_amd64"]
-            else:
-                platform_tags = ["win32", "win_amd64"]
+            platform_tags = ["win_amd64"] if machine_arch == "AMD64" else ["win32"]
 
         elif platform == "macos":
             if python_abi_tag.startswith("pp"):
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index efc1074cc..48723213b 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -106,7 +106,7 @@ def test_empty_selector(monkeypatch):
 @pytest.mark.parametrize(
     ("architecture", "image", "full_image"),
     [
-        ("x86_64", None, "quay.io/pypa/manylinux2014_x86_64:*"),
+        ("x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
         ("x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
@@ -114,7 +114,7 @@ def test_empty_selector(monkeypatch):
         ("i686", None, "quay.io/pypa/manylinux2014_i686:*"),
         ("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"),
         ("i686", "custom_image", "custom_image"),
-        ("pypy_x86_64", None, "quay.io/pypa/manylinux2014_x86_64:*"),
+        ("pypy_x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
         ("pypy_x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("pypy_x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index f17841fbe..4a24190c1 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -60,7 +60,7 @@ def test_build_default_launches(monkeypatch):
 
     # In Python 3.8+, this can be simplified to [0].kwargs
     kwargs = build_in_container.call_args_list[0][1]
-    assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
+    assert "quay.io/pypa/manylinux_2_28_x86_64" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
 
diff --git a/unit_test/options_toml_test.py b/unit_test/options_toml_test.py
index 03e125013..e5c8d5593 100644
--- a/unit_test/options_toml_test.py
+++ b/unit_test/options_toml_test.py
@@ -276,7 +276,7 @@ def test_environment_override_empty(tmp_path):
     assert options_reader.get("manylinux-i686-image") == ""
     assert options_reader.get("manylinux-aarch64-image") == "manylinux1"
 
-    assert options_reader.get("manylinux-x86_64-image", ignore_empty=True) == "manylinux2014"
+    assert options_reader.get("manylinux-x86_64-image", ignore_empty=True) == "manylinux_2_28"
     assert options_reader.get("manylinux-i686-image", ignore_empty=True) == "manylinux1"
     assert options_reader.get("manylinux-aarch64-image", ignore_empty=True) == "manylinux1"
 

From 2bb8b1e73d36474367641093c08ad7bffc9bb0a0 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 24 Mar 2025 20:58:34 +0100
Subject: [PATCH 026/193] [pre-commit.ci] pre-commit autoupdate (#2333)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.0 → v0.11.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 870f73522..7c34e7f59 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.0
+  rev: v0.11.2
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 5f4e019684661085adb6558969c7fd389a532174 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 24 Mar 2025 22:17:56 +0100
Subject: [PATCH 027/193] [2.x] Update dependencies (#2334)

---
 .../resources/constraints-pyodide312.txt      |  8 ++--
 .../resources/constraints-python310.txt       |  4 +-
 .../resources/constraints-python311.txt       |  4 +-
 .../resources/constraints-python312.txt       |  4 +-
 .../resources/constraints-python313.txt       |  4 +-
 .../resources/constraints-python39.txt        |  4 +-
 cibuildwheel/resources/constraints.txt        |  4 +-
 cibuildwheel/resources/nodejs.toml            |  2 +-
 .../resources/pinned_docker_images.cfg        | 42 +++++++++----------
 9 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 1ee368fca..7827ad07e 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -2,7 +2,7 @@
 #    nox -s update_constraints
 annotated-types==0.7.0
     # via pydantic
-anyio==4.8.0
+anyio==4.9.0
     # via httpx
 auditwheel-emscripten==0.0.16
     # via pyodide-build
@@ -23,7 +23,7 @@ cmake==3.31.6
     # via pyodide-build
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 h11==0.14.0
     # via httpcore
@@ -50,7 +50,7 @@ packaging==24.2
     #   unearth
 pip==25.0.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pydantic==2.10.6
     # via
@@ -98,7 +98,7 @@ typing-extensions==4.12.2
     #   pydantic
     #   pydantic-core
     #   typer
-unearth==0.17.2
+unearth==0.17.3
     # via pyodide-build
 urllib3==2.3.0
     # via requests
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index c6355b3ba..c7cd2fb8e 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 importlib-metadata==8.6.1
     # via build
@@ -20,7 +20,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index c1ba948c1..7b07dba10 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index c1ba948c1..7b07dba10 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index c1ba948c1..7b07dba10 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index c6355b3ba..c7cd2fb8e 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 importlib-metadata==8.6.1
     # via build
@@ -20,7 +20,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index c1ba948c1..7b07dba10 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -8,7 +8,7 @@ delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.9
     # via virtualenv
-filelock==3.17.0
+filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
@@ -18,7 +18,7 @@ packaging==24.2
     #   delocate
 pip==25.0.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.6
+platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
diff --git a/cibuildwheel/resources/nodejs.toml b/cibuildwheel/resources/nodejs.toml
index 91a71f773..ad93f2d7b 100644
--- a/cibuildwheel/resources/nodejs.toml
+++ b/cibuildwheel/resources/nodejs.toml
@@ -1,3 +1,3 @@
 url = "https://nodejs.org/dist/"
 v22 = "v22.14.0"
-v20 = "v20.18.3"
+v20 = "v20.19.0"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 4de150987..b7318293b 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,58 +1,58 @@
 [x86_64]
 manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.23-1
 
 [i686]
 manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.23-1
 
 [pypy_x86_64]
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
 
 [pypy_i686]
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.23-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.23-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.23-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.23-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.23-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.09-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.09-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.23-1
 

From d04cacbc9866d432033b1d09142936e6a0e2121a Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 24 Mar 2025 21:25:38 +0000
Subject: [PATCH 028/193] Bump version: v2.23.2

---
 README.md                              | 20 ++++++++------------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  7 +++++++
 docs/faq.md                            |  6 +++---
 docs/setup.md                          |  4 ++--
 examples/appveyor-minimal.yml          |  2 +-
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 19 files changed, 42 insertions(+), 39 deletions(-)

diff --git a/README.md b/README.md
index cd63c51ce..1d8b087df 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==2.23.1
+        run: python -m pip install cibuildwheel==2.23.2
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -215,6 +215,13 @@ Changelog
 
 <!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
 
+### v2.23.2
+
+_24 March 2025_
+
+- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
+- 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
+
 ### v2.23.1
 
 _15 March 2025_
@@ -257,17 +264,6 @@ _9 October 2024_
 - 🛠 Update CPython 3.13 to 3.13.0 final release (#2032)
 - 📚 Docs updates and tidy ups (#2035)
 
-### v2.21.2
-
-_2 October 2024_
-
-- ✨ Adds support for building 32-bit armv7l wheels on musllinux. On a Linux system with emulation set up, set [CIBW_ARCHS](https://cibuildwheel.pypa.io/en/stable/options/#archs) to `armv7l` on Linux to try it out if you're interested! (#2017)
-- 🐛 Fix Linux Podman builds on some systems (#2016)
-- ✨ Adds official support for running on Python 3.13 (#2026)
-- 🛠 Update CPython 3.13 to 3.13.0rc3 (#2029)
-
-Note: the default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) is **scheduled to change** from `manylinux2014` to `manylinux_2_28` in a cibuildwheel release on or after **6th May 2025** - you can set the value now to avoid getting upgraded if you want. (#1992)
-
 <!-- END bin/update_readme_changelog.py -->
 
 ---
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 542a94157..de14b4cba 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1,3 +1,3 @@
 from __future__ import annotations
 
-__version__ = "2.23.1"
+__version__ = "2.23.2"
diff --git a/docs/changelog.md b/docs/changelog.md
index 87f40492d..b9234b943 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,13 @@ title: Changelog
 
 # Changelog
 
+### v2.23.2
+
+_24 March 2025_
+
+- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
+- 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
+
 ### v2.23.1
 
 _15 March 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 8df6278e9..86835d6ef 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -142,7 +142,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v2.23.1
+uses: pypa/cibuildwheel@v2.23.2
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -164,7 +164,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==2.23.1
+cibuildwheel==2.23.2
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -328,7 +328,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==2.23.1 --output-dir wheelhouse
+pipx run cibuildwheel==2.23.2 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/docs/setup.md b/docs/setup.md
index 93c3d95ed..29e8a380e 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -161,7 +161,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
           - uses: actions/checkout@v4
 
           - name: Build wheels
-            run: pipx run cibuildwheel==2.23.1
+            run: pipx run cibuildwheel==2.23.2
 
           - uses: actions/upload-artifact@v4
             with:
@@ -198,7 +198,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
           - uses: actions/setup-python@v5
 
           - name: Install cibuildwheel
-            run: python -m pip install cibuildwheel==2.23.1
+            run: python -m pip install cibuildwheel==2.23.2
 
           - name: Build wheels
             run: python -m cibuildwheel --output-dir wheelhouse
diff --git a/examples/appveyor-minimal.yml b/examples/appveyor-minimal.yml
index e1c9d39b6..b9c9e11c0 100644
--- a/examples/appveyor-minimal.yml
+++ b/examples/appveyor-minimal.yml
@@ -9,7 +9,7 @@ environment:
 
 stack: python 3.12
 
-install: python -m pip install cibuildwheel==2.23.1
+install: python -m pip install cibuildwheel==2.23.2
 
 build_script: python -m cibuildwheel --output-dir wheelhouse
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 1aed664d8..29c3bc603 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==2.23.1
+        pip3 install cibuildwheel==2.23.2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==2.23.1
+        python3 -m pip install cibuildwheel==2.23.2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==2.23.1
+        pip install cibuildwheel==2.23.2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index f0bfec4f4..1265b26fb 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.1
+            python3 -m pip install --user cibuildwheel==2.23.2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.1
+            python3 -m pip install --user cibuildwheel==2.23.2
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==2.23.1
+            pip3 install cibuildwheel==2.23.2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index c8f66a189..9fe287a42 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.1
+    - python -m pip install cibuildwheel==2.23.2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 7bfb28cad..59f724c2c 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.1
+    - python -m pip install cibuildwheel==2.23.2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index c2060150f..5d105a58a 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -23,7 +23,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.1
+        uses: pypa/cibuildwheel@v2.23.2
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index fb1a52626..7ad9e610d 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.1
+        uses: pypa/cibuildwheel@v2.23.2
         # env:
         #   CIBW_SOME_OPTION: value
         #    ...
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index d79145715..fd0126a74 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.1
+        uses: pypa/cibuildwheel@v2.23.2
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 7018a9c1f..b4268771b 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==2.23.1
+    - python -m pip install cibuildwheel==2.23.2
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==2.23.1
+    - py -m pip install cibuildwheel==2.23.2
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==2.23.1
+    - python3 -m pip install cibuildwheel==2.23.2
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 0e7484167..36cc5e4cc 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==2.23.1
+    - python -m pip install cibuildwheel==2.23.2
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index 88e1923ae..82959ee67 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.1
+  - python3 -m pip install cibuildwheel==2.23.2
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 0db795b14..45aa1c4ee 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.1
+  - python3 -m pip install cibuildwheel==2.23.2
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 6922a50bf..19a6e1fa8 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==2.23.1 twine
+      install: python3 -m pip install cibuildwheel==2.23.2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==2.23.1 twine
+      install: python3 -m pip install cibuildwheel==2.23.2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 25ad14548..e85583614 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "2.23.1"
+version = "2.23.2"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 4ef7b3770f3ebfdd8025f269b16c3dd5d4080481 Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Fri, 28 Mar 2025 22:33:51 +0800
Subject: [PATCH 029/193] ci: correct Github Actions CI instability for iOS.
 (#2337)

* Correct Github Actions CI instability for iOS.

* Add a dummy serial test so CircleCI passes serial test discovery.
---
 .github/workflows/test.yml |  2 +-
 bin/run_tests.py           | 34 +++++++++++++++++++++++++++++++---
 pyproject.toml             |  4 +++-
 test/test_0_basic.py       |  9 +++++++++
 test/test_ios.py           | 13 ++++++++-----
 5 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 6d690562d..23c054423 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -36,7 +36,7 @@ jobs:
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14]
+        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-15]
         python_version: ['3.13']
         include:
         - os: ubuntu-latest
diff --git a/bin/run_tests.py b/bin/run_tests.py
index 2aed4f01a..bbfbc25a5 100755
--- a/bin/run_tests.py
+++ b/bin/run_tests.py
@@ -34,15 +34,39 @@
         if args.run_podman:
             unit_test_args += ["--run-podman"]
 
+    print(
+        "\n\n================================== UNIT TESTS ==================================",
+        flush=True,
+    )
     subprocess.run(unit_test_args, check=True)
 
-    # integration tests
+    # Run the serial integration tests without multiple processes
+    serial_integration_test_args = [
+        sys.executable,
+        "-m",
+        "pytest",
+        "-m",
+        "serial",
+        "-x",
+        "--durations",
+        "0",
+        "--timeout=2400",
+        "test",
+        "-vv",
+    ]
+    print(
+        "\n\n=========================== SERIAL INTEGRATION TESTS ===========================",
+        flush=True,
+    )
+    subprocess.run(serial_integration_test_args, check=True)
+
+    # Non-serial integration tests
     integration_test_args = [
         sys.executable,
         "-m",
         "pytest",
-        "--dist",
-        "loadgroup",
+        "-m",
+        "not serial",
         f"--numprocesses={args.num_processes}",
         "-x",
         "--durations",
@@ -55,4 +79,8 @@
     if sys.platform.startswith("linux") and args.run_podman:
         integration_test_args += ["--run-podman"]
 
+    print(
+        "\n\n========================= NON-SERIAL INTEGRATION TESTS =========================",
+        flush=True,
+    )
     subprocess.run(integration_test_args, check=True)
diff --git a/pyproject.toml b/pyproject.toml
index 0319e2e09..e3efab7ca 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -102,7 +102,9 @@ junit_family = "xunit2"
 xfail_strict = true
 filterwarnings = ["error"]
 log_cli_level = "info"
-
+markers = [
+    "serial: tests that must *not* be run in parallel (deselect with '-m \"not serial\"')",
+]
 
 [tool.mypy]
 python_version = "3.11"
diff --git a/test/test_0_basic.py b/test/test_0_basic.py
index 4ef40a2c2..6a7732031 100644
--- a/test/test_0_basic.py
+++ b/test/test_0_basic.py
@@ -18,6 +18,15 @@
 )
 
 
+@pytest.mark.serial
+def test_dummy_serial():
+    """A no-op test to ensure that at least one serial test is always found.
+
+    Without this no-op test, CI fails on CircleCI because no serial tests are
+    found, and pytest errors if a test suite finds no tests.
+    """
+
+
 def test(tmp_path, build_frontend_env, capfd):
     project_dir = tmp_path / "project"
     basic_project.generate(project_dir)
diff --git a/test/test_ios.py b/test/test_ios.py
index 6dc7b40d3..ca6bc7ab2 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -20,10 +20,12 @@ def test_platform(self):
 """
 
 
-# iOS tests shouldn't be run in parallel, because they're dependent on starting
-# a simulator. It's *possible* to start multiple simulators, but not advisable
-# to start as many simulators as there are CPUs on the test machine.
-@pytest.mark.xdist_group(name="ios")
+# iOS tests shouldn't be run in parallel, because they're dependent on calling
+# Xcode, and starting a simulator. These are both multi-threaded operations, and
+# it's easy to overload the CI machine if there are multiple test processes
+# running multithreaded processes. Therefore, they're put in the serial group,
+# which is guaranteed to run single-process.
+@pytest.mark.serial
 @pytest.mark.parametrize(
     "build_config",
     [
@@ -48,6 +50,7 @@ def test_ios_platforms(tmp_path, build_config):
             "CIBW_BUILD": "cp313-*",
             "CIBW_TEST_SOURCES": "tests",
             "CIBW_TEST_COMMAND": "unittest discover tests test_platform.py",
+            "CIBW_BUILD_VERBOSITY": "1",
             **build_config,
         },
     )
@@ -71,7 +74,7 @@ def test_ios_platforms(tmp_path, build_config):
     assert set(actual_wheels) == expected_wheels
 
 
-@pytest.mark.xdist_group(name="ios")
+@pytest.mark.serial
 def test_no_test_sources(tmp_path, capfd):
     if utils.platform != "macos":
         pytest.skip("this test can only run on macOS")

From 0dd98e95f17ddbec5eb93cca386efaa1a75e0bd3 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Sun, 30 Mar 2025 10:44:32 +0200
Subject: [PATCH 030/193] ci: revert QEMU workaround (#2340)

---
 .github/workflows/test.yml | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 23c054423..675831734 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -63,11 +63,6 @@ jobs:
     - name: Set up QEMU
       if: runner.os == 'Linux'
       uses: docker/setup-qemu-action@v3
-      with:
-        # 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: Install dependencies
       run: |
@@ -169,11 +164,6 @@ jobs:
 
     - name: Set up QEMU
       uses: docker/setup-qemu-action@v3
-      with:
-        # 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: Run the emulation tests
       run: uv run pytest --run-emulation ${{ matrix.arch }} test/test_emulation.py

From 279221e0d6a3e163c302b8b47de676f14a4fec11 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 31 Mar 2025 12:46:45 -0400
Subject: [PATCH 031/193] [Bot] Update dependencies (#2341)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      | 13 +++--
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python38.txt        |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 9 files changed, 42 insertions(+), 39 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 7827ad07e..3aec20f72 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -19,7 +19,7 @@ charset-normalizer==3.4.1
     # via requests
 click==8.1.8
     # via typer
-cmake==3.31.6
+cmake==4.0.0
     # via pyodide-build
 distlib==0.3.9
     # via virtualenv
@@ -52,11 +52,11 @@ pip==25.0.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
 platformdirs==4.3.7
     # via virtualenv
-pydantic==2.10.6
+pydantic==2.11.1
     # via
     #   pyodide-build
     #   pyodide-lock
-pydantic-core==2.27.2
+pydantic-core==2.33.0
     # via pydantic
 pygments==2.19.1
     # via rich
@@ -74,7 +74,7 @@ requests==2.32.3
     # via pyodide-build
 resolvelib==1.1.0
     # via pyodide-build
-rich==13.9.4
+rich==14.0.0
     # via
     #   pyodide-build
     #   pyodide-cli
@@ -92,12 +92,15 @@ typer==0.15.2
     #   auditwheel-emscripten
     #   pyodide-build
     #   pyodide-cli
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via
     #   anyio
     #   pydantic
     #   pydantic-core
     #   typer
+    #   typing-inspection
+typing-inspection==0.4.0
+    # via pydantic
 unearth==0.17.3
     # via pyodide-build
 urllib3==2.3.0
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index c7cd2fb8e..266f34972 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via delocate
 virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 7b07dba10..3cc1e991e 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via delocate
 virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 7b07dba10..3cc1e991e 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via delocate
 virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 7b07dba10..3cc1e991e 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via delocate
 virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 72784c87f..f49fdd3ba 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via delocate
 virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index c7cd2fb8e..266f34972 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via delocate
 virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 7b07dba10..3cc1e991e 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.0
     # via delocate
 virtualenv==20.29.3
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 82c484a71..bd1f0b0a6 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,45 +1,45 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.30-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.30-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.30-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.30-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.30-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.30-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.30-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.30-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.30-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.30-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.30-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.30-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.30-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.30-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.30-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.30-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.30-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.30-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.30-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.30-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.30-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.30-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.30-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.30-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.30-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.23-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.30-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.30-1
 

From 55bd91a92aa7ffa4e600b692f4ab096ef5932147 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 1 Apr 2025 10:25:09 -0400
Subject: [PATCH 032/193] [pre-commit.ci] pre-commit autoupdate (#2343)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/python-jsonschema/check-jsonschema: 0.31.3 → 0.32.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.3...0.32.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7c34e7f59..73e18b3eb 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -81,7 +81,7 @@ repos:
 
 
 - repo: https://github.com/python-jsonschema/check-jsonschema
-  rev: 0.31.3
+  rev: 0.32.1
   hooks:
     - id: check-dependabot
     - id: check-github-actions

From 6c426a3a17cfcadf4b6048de53653eba55d7ae4f Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Tue, 1 Apr 2025 23:28:17 -0400
Subject: [PATCH 033/193] [Bot] Update dependencies (#2346)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/constraints-pyodide312.txt | 2 +-
 cibuildwheel/resources/constraints-python310.txt  | 2 +-
 cibuildwheel/resources/constraints-python311.txt  | 2 +-
 cibuildwheel/resources/constraints-python312.txt  | 2 +-
 cibuildwheel/resources/constraints-python313.txt  | 2 +-
 cibuildwheel/resources/constraints-python38.txt   | 2 +-
 cibuildwheel/resources/constraints-python39.txt   | 2 +-
 cibuildwheel/resources/constraints.txt            | 2 +-
 cibuildwheel/resources/virtualenv.toml            | 2 +-
 docs/working-examples.md                          | 4 ++--
 10 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 3aec20f72..7ae6131b1 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -105,7 +105,7 @@ unearth==0.17.3
     # via pyodide-build
 urllib3==2.3.0
     # via requests
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via
     #   build
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 266f34972..b5668a46c 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.13.0
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 3cc1e991e..9520878ef 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.0
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 3cc1e991e..9520878ef 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.0
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 3cc1e991e..9520878ef 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.0
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index f49fdd3ba..274b1f2b6 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.13.0
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.20.2
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 266f34972..b5668a46c 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.13.0
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 3cc1e991e..9520878ef 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.0
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index 0d89df7aa..e9b284617 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1 +1 @@
-default = { version = "20.29.3", url = "https://github.com/pypa/get-virtualenv/blob/20.29.3/public/virtualenv.pyz?raw=true" }
+default = { version = "20.30.0", url = "https://github.com/pypa/get-virtualenv/blob/20.30.0/public/virtualenv.pyz?raw=true" }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 9ed006bca..ed5d48ddf 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -108,8 +108,8 @@ title: Working examples
 | [ninja][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
 | [numpythia][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
 | [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
-| [SiPM][]                          | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
 | [ril][]                           | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
+| [SiPM][]                          | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
 | [GSD][]                           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
 | [aalink][]                        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
 | [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
@@ -218,8 +218,8 @@ title: Working examples
 [ninja]: https://github.com/scikit-build/ninja-python-distributions
 [numpythia]: https://github.com/scikit-hep/numpythia
 [pyjet]: https://github.com/scikit-hep/pyjet
-[SiPM]: https://github.com/EdoPro98/SimSiPM
 [ril]: https://github.com/Cryptex-github/ril-py
+[SiPM]: https://github.com/EdoPro98/SimSiPM
 [GSD]: https://github.com/glotzerlab/gsd
 [aalink]: https://github.com/artfwo/aalink
 [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib

From 2aaa489371aaddaf5f930c508e04673318b7ce2d Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sun, 6 Apr 2025 12:50:24 -0400
Subject: [PATCH 034/193] fix: support wider range of verbosity settings on
 other build backends (#2339)

* fix: support wider range of verbosity settings on other build backends

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

* refactor: address review comments

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
---
 cibuildwheel/frontend.py  | 15 ++++++++----
 docs/options.md           | 28 +++++++++++++++++++----
 unit_test/options_test.py | 48 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/cibuildwheel/frontend.py b/cibuildwheel/frontend.py
index 8e6a2897f..79796a1be 100644
--- a/cibuildwheel/frontend.py
+++ b/cibuildwheel/frontend.py
@@ -37,14 +37,19 @@ def options_summary(self) -> str | dict[str, str]:
 
 
 def _get_verbosity_flags(level: int, frontend: BuildFrontendName) -> list[str]:
-    if frontend == "pip":
-        if level > 0:
-            return ["-" + level * "v"]
-        if level < 0:
+    if level < 0:
+        if frontend == "pip":
             return ["-" + -level * "q"]
-    elif not 0 <= level < 2:
+
         msg = f"build_verbosity {level} is not supported for {frontend} frontend. Ignoring."
         log.warning(msg)
+
+    if level > 0:
+        if frontend == "pip":
+            return ["-" + level * "v"]
+        if level > 1:
+            return ["-" + (level - 1) * "v"]
+
     return []
 
 
diff --git a/docs/options.md b/docs/options.md
index 0c4ade208..fe2adad84 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1760,9 +1760,27 @@ export CIBW_DEBUG_TRACEBACK=TRUE
 ```
 
 ### `CIBW_BUILD_VERBOSITY` {: #build-verbosity}
-> Increase/decrease the output of pip wheel
-
-A number from 1 to 3 to increase the level of verbosity (corresponding to invoking pip with `-v`, `-vv`, and `-vvv`), between -1 and -3 (`-q`, `-qq`, and `-qqq`), or just 0 (default verbosity). These flags are useful while debugging a build when the output of the actual build invoked by `pip wheel` is required. Has no effect on the `build` backend, which produces verbose output by default.
+> Increase/decrease the output of the build
+
+This setting controls `-v`/`-q` flags to the build frontend. Since there is
+no communication between the build backend and the build frontend, build
+messages from the build backend will always be shown with `1`; higher levels
+will not produce more logging about the build itself. Other levels only affect
+the build frontend output, which is usually things like resolving and
+downloading dependencies. The settings are:
+
+|             | build | pip    | desc                             |
+|-------------|-------|--------|----------------------------------|
+| -2          | N/A   | `-qq`  | even more quiet, where supported |
+| -1          | N/A   | `-q`   | quiet mode, where supported      |
+| 0 (default) |       |        | default for build tool           |
+| 1           |       | `-v`   | print backend output             |
+| 2           | `-v`  | `-vv`  | print log messages e.g. resolving info |
+| 3           | `-vv` | `-vvv` | print even more debug info       |
+
+Settings that are not supported for a specific frontend will log a warning.
+The default build frontend is `build`, which does show build backend output by
+default.
 
 Platform-specific environment variables are also available:<br/>
 `CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX` | `CIBW_BUILD_VERBOSITY_IOS` | `CIBW_BUILD_VERBOSITY_PYODIDE`
@@ -1772,7 +1790,7 @@ Platform-specific environment variables are also available:<br/>
 !!! tab examples "Environment variables"
 
     ```yaml
-    # Increase pip debugging output
+    # Ensure that the build backend output is present
     CIBW_BUILD_VERBOSITY: 1
     ```
 
@@ -1780,7 +1798,7 @@ Platform-specific environment variables are also available:<br/>
 
     ```toml
     [tool.cibuildwheel]
-    # Increase pip debugging output
+    # Ensure that the build backend output is present
     build-verbosity = 1
     ```
 
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index 143f7fdc0..bcf242916 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -1,12 +1,16 @@
 import os
 import platform as platform_module
 import textwrap
+import unittest.mock
 from pathlib import Path
+from typing import Literal
 
 import pytest
 
 from cibuildwheel import errors
 from cibuildwheel.bashlex_eval import local_environment_executor
+from cibuildwheel.frontend import BuildFrontendConfig, get_build_frontend_extra_flags
+from cibuildwheel.logger import Logger
 from cibuildwheel.options import (
     CommandLineArguments,
     Options,
@@ -570,3 +574,47 @@ def test_deprecated_image(
         assert f"{resolved_image!r}" in captured.err
     else:
         assert "Deprecated image" not in captured.err
+
+
+@pytest.mark.parametrize(
+    ("frontend", "verbosity", "result"),
+    [
+        ("pip", 3, ["-Ca", "-Cb", "-1", "-vvv"]),
+        ("pip", 2, ["-Ca", "-Cb", "-1", "-vv"]),
+        ("pip", -1, ["-Ca", "-Cb", "-1", "-q"]),
+        ("build", 0, ["-Ca", "-Cb", "-1"]),
+        ("build", 1, ["-Ca", "-Cb", "-1"]),
+        ("build", 2, ["-Ca", "-Cb", "-1", "-v"]),
+        ("build", 3, ["-Ca", "-Cb", "-1", "-vv"]),
+        ("build[uv]", 3, ["-Ca", "-Cb", "-1", "-vv"]),
+    ],
+)
+def test_get_build_frontend_extra_flags(
+    frontend: Literal["pip", "build", "build[uv]"],
+    verbosity: int,
+    result: list[str],
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:
+    mock_warning = unittest.mock.MagicMock()
+    monkeypatch.setattr(Logger, "warning", mock_warning)
+    build_frontend = BuildFrontendConfig(frontend, ["-1"])
+    args = get_build_frontend_extra_flags(
+        build_frontend=build_frontend, verbosity_level=verbosity, config_settings="a b"
+    )
+
+    assert args == result
+    mock_warning.assert_not_called()
+
+
+@pytest.mark.parametrize("frontend", ["build", "build[uv]"])
+def test_get_build_frontend_extra_flags_warning(
+    frontend: Literal["build", "build[uv]"], monkeypatch: pytest.MonkeyPatch
+) -> None:
+    mock_warning = unittest.mock.MagicMock()
+    monkeypatch.setattr(Logger, "warning", mock_warning)
+    build_frontend = BuildFrontendConfig(frontend, ["-1"])
+    args = get_build_frontend_extra_flags(
+        build_frontend=build_frontend, verbosity_level=-1, config_settings="a b"
+    )
+    assert args == ["-Ca", "-Cb", "-1"]
+    mock_warning.assert_called_once()

From 830e79e11cbb1ad97735febb13526bd2ca574e86 Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Mon, 7 Apr 2025 14:20:20 +0800
Subject: [PATCH 035/193] feat: add the ability to declare safe tools in a
 cross-build environment. (#2317)

* Add the ability to declare safe tools in a cross-build environment.

* Add an xfail if cmake isn't available on the test machine.

* Placate linter regarding positional args.

* Rework test to provide more robust confirmation of safe tools.

* Remove a test skip condition that is no longer needed.

Co-authored-by: Joe Rickerby <joerick@mac.com>

* Rename the setting to xbuild-tools.

* Add docs to clarify that xbuild-tools is transitive.

* Raise a warning if xbuild-tools isn't defined.

* Correct a bad copy-paste in the schema generator.

* .. and now fix the indentation.

* Move sentinel handling earlier into the parsing process.

* Remove serialization from tests that won't start a test suite.

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 README.md                                     |   1 +
 bin/generate_schema.py                        |   6 +-
 cibuildwheel/options.py                       |  14 ++
 cibuildwheel/platforms/ios.py                 |  60 ++++++-
 .../resources/cibuildwheel.schema.json        |  21 +++
 cibuildwheel/resources/defaults.toml          |   2 +
 docs/options.md                               |  43 +++++
 docs/platforms/ios.md                         |   4 +-
 test/test_ios.py                              | 161 +++++++++++++++++-
 unit_test/options_test.py                     |  28 +++
 unit_test/options_toml_test.py                |  11 ++
 11 files changed, 336 insertions(+), 15 deletions(-)

diff --git a/README.md b/README.md
index c70d40f9b..272815885 100644
--- a/README.md
+++ b/README.md
@@ -139,6 +139,7 @@ Options
 |   | [`CIBW_ENVIRONMENT_PASS_LINUX`](https://cibuildwheel.pypa.io/en/stable/options/#environment-pass)  | Set environment variables on the host to pass-through to the container during the build. |
 |   | [`CIBW_BEFORE_ALL`](https://cibuildwheel.pypa.io/en/stable/options/#before-all)  | Execute a shell command on the build system before any wheels are built. |
 |   | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.pypa.io/en/stable/options/#before-build)  | Execute a shell command preparing each wheel's build |
+|   | [`CIBW_XBUILD_TOOLS`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools)  | Binaries on the path that should be included in an isolated cross-build environment. |
 |   | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command)  | Execute a shell command to repair each built wheel |
 |   | [`CIBW_MANYLINUX_*_IMAGE`<br/>`CIBW_MUSLLINUX_*_IMAGE`](https://cibuildwheel.pypa.io/en/stable/options/#linux-image)  | Specify alternative manylinux / musllinux Docker images |
 |   | [`CIBW_CONTAINER_ENGINE`](https://cibuildwheel.pypa.io/en/stable/options/#container-engine)  | Specify which container engine to use when building Linux wheels |
diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index b82a91136..8ca6a1a76 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -181,9 +181,12 @@
   musllinux-x86_64-image:
     type: string
     description: Specify alternative manylinux / musllinux container images
-  repair-wheel-command:
+  xbuild-tools:
+    description: Binaries on the path that should be included in an isolated cross-build environment
     type: string_array
+  repair-wheel-command:
     description: Execute a shell command to repair each built wheel.
+    type: string_array
   skip:
     description: Choose the Python versions to skip.
     type: string_array
@@ -273,6 +276,7 @@
       properties:
         before-all: {"$ref": "#/$defs/inherit"}
         before-build: {"$ref": "#/$defs/inherit"}
+        xbuild-tools: {"$ref": "#/$defs/inherit"}
         before-test: {"$ref": "#/$defs/inherit"}
         config-settings: {"$ref": "#/$defs/inherit"}
         container-engine: {"$ref": "#/$defs/inherit"}
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 78cbaf519..1e07a3f97 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -94,6 +94,7 @@ class BuildOptions:
     environment: ParsedEnvironment
     before_all: str
     before_build: str | None
+    xbuild_tools: list[str] | None
     repair_command: str
     manylinux_images: dict[str, str] | None
     musllinux_images: dict[str, str] | None
@@ -718,6 +719,18 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
 
             test_command = self.reader.get("test-command", option_format=ListFormat(sep=" && "))
             before_test = self.reader.get("before-test", option_format=ListFormat(sep=" && "))
+            xbuild_tools: list[str] | None = shlex.split(
+                self.reader.get(
+                    "xbuild-tools", option_format=ListFormat(sep=" ", quote=shlex.quote)
+                )
+            )
+            # ["\u0000"] is a sentinel value used as a default, because TOML
+            # doesn't have an explicit NULL value. If xbuild-tools is set to the
+            # sentinel, it indicates that the user hasn't defined xbuild-tools
+            # *at all* (not even an `xbuild-tools = []` definition).
+            if xbuild_tools == ["\u0000"]:
+                xbuild_tools = None
+
             test_sources = shlex.split(
                 self.reader.get(
                     "test-sources", option_format=ListFormat(sep=" ", quote=shlex.quote)
@@ -835,6 +848,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                 before_build=before_build,
                 before_all=before_all,
                 build_verbosity=build_verbosity,
+                xbuild_tools=xbuild_tools,
                 repair_command=repair_command,
                 environment=environment,
                 dependency_constraints=dependency_constraints,
diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 25dc2a461..06fce61fa 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -5,6 +5,7 @@
 import shutil
 import subprocess
 import sys
+import textwrap
 from collections.abc import Sequence, Set
 from dataclasses import dataclass
 from pathlib import Path
@@ -151,6 +152,7 @@ def cross_virtualenv(
     build_python: Path,
     venv_path: Path,
     dependency_constraint_flags: Sequence[PathOrStr],
+    xbuild_tools: Sequence[str] | None,
 ) -> dict[str, str]:
     """Create a cross-compilation virtual environment.
 
@@ -178,6 +180,8 @@ def cross_virtualenv(
         created.
     :param dependency_constraint_flags: Any flags that should be used when
         constraining dependencies in the environment.
+    :param xbuild_tools: A list of executable names (without paths) that are
+        on the path, but must be preserved in the cross environment.
     """
     # Create an initial macOS virtual environment
     env = virtualenv(
@@ -210,14 +214,52 @@ def cross_virtualenv(
     #
     # To prevent problems, set the PATH to isolate the build environment from
     # sources that could introduce incompatible binaries.
+    #
+    # However, there may be some tools on the path that are needed for the
+    # build. Find their location on the path, and link the underlying binaries
+    # (fully resolving symlinks) to a "safe" location that will *only* contain
+    # those tools. This avoids needing to add *all* of Homebrew to the path just
+    # to get access to (for example) cmake for build purposes. A value of None
+    # means the user hasn't provided a list of xbuild tools.
+    xbuild_tools_path = venv_path / "cibw_xbuild_tools"
+    xbuild_tools_path.mkdir()
+    if xbuild_tools is None:
+        log.warning(
+            textwrap.dedent(
+                """
+                Your project configuration does not define any cross-build tools.
+
+                iOS builds use an isolated build environment; if your build process requires any
+                third-party tools (such as cmake, ninja, or rustc), you must explicitly declare
+                that those tools are required using xbuild-tools/CIBW_XBUILD_TOOLS. This will
+                likely manifest as a "somebuildtool: command not found" error.
+
+                If the build succeeds, you can silence this warning by setting adding
+                `xbuild-tools = []` to your pyproject.toml configuration, or exporting
+                CIBW_XBUILD_TOOLS as an empty string into your environment.
+                """
+            )
+        )
+    else:
+        for tool in xbuild_tools:
+            tool_path = shutil.which(tool)
+            if tool_path is None:
+                msg = f"Could not find a {tool!r} executable on the path."
+                raise errors.FatalError(msg)
+
+            # Link the binary into the safe tools directory
+            original = Path(tool_path).resolve()
+            print(f"{tool!r} will be included in the cross-build environment (using {original})")
+            (xbuild_tools_path / tool).symlink_to(original)
+
     env["PATH"] = os.pathsep.join(
         [
             # The target python's binary directory
             str(target_python.parent),
-            # The cross-platform environments binary directory
+            # The cross-platform environment's binary directory
             str(venv_path / "bin"),
-            # Cargo's binary directory (to allow for Rust compilation)
-            str(Path.home() / ".cargo" / "bin"),
+            # The directory of cross-build tools
+            str(xbuild_tools_path),
             # The bare minimum Apple system paths.
             "/usr/bin",
             "/bin",
@@ -235,10 +277,12 @@ def cross_virtualenv(
 
 def setup_python(
     tmp: Path,
+    *,
     python_configuration: PythonConfiguration,
     dependency_constraint_flags: Sequence[PathOrStr],
     environment: ParsedEnvironment,
     build_frontend: BuildFrontendName,
+    xbuild_tools: Sequence[str] | None,
 ) -> tuple[Path, dict[str, str]]:
     if build_frontend == "build[uv]":
         msg = "uv doesn't support iOS"
@@ -291,6 +335,7 @@ def setup_python(
         build_python=build_python,
         venv_path=venv_path,
         dependency_constraint_flags=dependency_constraint_flags,
+        xbuild_tools=xbuild_tools,
     )
     venv_bin_path = venv_path / "bin"
     assert venv_bin_path.exists()
@@ -414,10 +459,11 @@ def build(options: Options, tmp_path: Path) -> None:
 
             target_install_path, env = setup_python(
                 identifier_tmp_dir / "build",
-                config,
-                dependency_constraint_flags,
-                build_options.environment,
-                build_frontend.name,
+                python_configuration=config,
+                dependency_constraint_flags=dependency_constraint_flags,
+                environment=build_options.environment,
+                build_frontend=build_frontend.name,
+                xbuild_tools=build_options.xbuild_tools,
             )
             pip_version = get_pip_version(env)
 
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 4a84606cf..10e07cc15 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -397,6 +397,21 @@
       "description": "Specify alternative manylinux / musllinux container images",
       "title": "CIBW_MUSLLINUX_X86_64_IMAGE"
     },
+    "xbuild-tools": {
+      "description": "Binaries on the path that should be included in an isolated cross-build environment",
+      "oneOf": [
+        {
+          "type": "string"
+        },
+        {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      ],
+      "title": "CIBW_XBUILD_TOOLS"
+    },
     "repair-wheel-command": {
       "description": "Execute a shell command to repair each built wheel.",
       "oneOf": [
@@ -566,6 +581,9 @@
               "environment-pass": {
                 "$ref": "#/$defs/inherit"
               },
+              "xbuild-tools": {
+                "$ref": "#/$defs/inherit"
+              },
               "repair-wheel-command": {
                 "$ref": "#/$defs/inherit"
               },
@@ -991,6 +1009,9 @@
         "repair-wheel-command": {
           "$ref": "#/properties/repair-wheel-command"
         },
+        "xbuild-tools": {
+          "$ref": "#/properties/xbuild-tools"
+        },
         "test-command": {
           "$ref": "#/properties/test-command"
         },
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index bd17245b4..8ca424556 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -14,6 +14,8 @@ build-verbosity = 0
 
 before-all = ""
 before-build = ""
+# TOML doesn't support explicit NULLs; use ["\u0000"] as a sentinel value.
+xbuild-tools = ["\u0000"]
 repair-wheel-command = ""
 
 test-command = ""
diff --git a/docs/options.md b/docs/options.md
index fe2adad84..cee3b6d7c 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1043,6 +1043,49 @@ Platform-specific environment variables are also available:<br/>
     [PEP 517]: https://www.python.org/dev/peps/pep-0517/
     [PEP 518]: https://www.python.org/dev/peps/pep-0517/
 
+### `CIBW_XBUILD_TOOLS` {: #xbuild-tools}
+> Binaries on the path that should be included in an isolated cross-build environment.
+
+When building in a cross-platform environment, it is sometimes necessary to isolate the ``PATH`` so that binaries from the build machine don't accidentally get linked into the cross-platform binary. However, this isolation process will also hide tools that might be required to build your wheel.
+
+If there are binaries present on the `PATH` when you invoke cibuildwheel, and those binaries are required to build your wheels, those binaries can be explicitly included in the isolated cross-build environment using `CIBW_XBUILD_TOOLS`. The binaries listed in this setting will be linked into an isolated location, and that isolated location will be put on the `PATH` of the isolated environment. You do not need to provide the full path to the binary - only the executable name that would be found by the shell.
+
+If you declare a tool as a cross-build tool, and that tool cannot be found in the runtime environment, an error will be raised.
+
+If you do not define `CIBW_XBUILD_TOOLS`, and you build for a platform that uses a cross-platform environment, a warning will be raised. If your project does not require any cross-build tools, you can set `CIBW_XBUILD_TOOLS` to an empty list to silence this warning.
+
+*Any* tool used by the build process must be included in the `CIBW_XBUILD_TOOLS` list, not just tools that cibuildwheel will invoke directly. For example, if your build invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your safe tools list.
+
+Platform-specific environment variables are also available on platforms that use cross-platform environment isolation:<br/>
+ `CIBW_XBUILD_TOOLS_IOS`
+
+#### Examples
+
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Allow access to the cmake and rustc binaries in the isolated cross-build environment.
+    CIBW_XBUILD_TOOLS: cmake rustc
+    ```
+
+    ```yaml
+    # No cross-build tools are required
+    CIBW_XBUILD_TOOLS:
+    ```
+
+!!! tab examples "pyproject.toml"
+
+    ```toml
+    [tool.cibuildwheel]
+    # Allow access to the cmake and rustc binaries in the isolated cross-build environment.
+    xbuild-tools = ["cmake", "rustc"]
+    ```
+
+    ```toml
+    [tool.cibuildwheel]
+    # No cross-build tools are required
+    xbuild-tools = []
+    ```
 
 ### `CIBW_REPAIR_WHEEL_COMMAND` {: #repair-wheel-command}
 > Execute a shell command to repair each built wheel
diff --git a/docs/platforms/ios.md b/docs/platforms/ios.md
index a58c0132b..0c93ba052 100644
--- a/docs/platforms/ios.md
+++ b/docs/platforms/ios.md
@@ -57,7 +57,9 @@ iOS builds support both the `pip` and `build` build frontends. In principle, sup
 
 ## Build environment
 
-The environment used to run builds does not inherit the full user environment - in particular, `PATH` is deliberately re-written. This is because UNIX C tooling doesn't do a great job differentiating between "macOS ARM64" and "iOS ARM64" binaries. If (for example) Homebrew is on the path when compilation commands are invoked, it's easy for a macOS version of a library to be linked into the iOS binary, rendering it unusable on iOS. To prevent this, iOS builds always force `PATH` to a "known minimal" path, that includes only the bare system utilities, plus the current user's cargo folder (to facilitate Rust builds).
+The environment used to run builds does not inherit the full user environment - in particular, `PATH` is deliberately re-written. This is because UNIX C tooling doesn't do a great job differentiating between "macOS ARM64" and "iOS ARM64" binaries. If (for example) Homebrew is on the path when compilation commands are invoked, it's easy for a macOS version of a library to be linked into the iOS binary, rendering it unusable on iOS. To prevent this, iOS builds always force `PATH` to a "known minimal" path, that includes only the bare system utilities, and the iOS compiler toolchain.
+
+If your project requires additional tools to build (such as `cmake`, `ninja`, or `rustc`), those tools must be explicitly declared as cross-build tools using [`CIBW_XBUILD_TOOLS`](../../options#xbuild-tools). *Any* tool used by the build process must be included in the `CIBW_XBUILD_TOOLS` list, not just tools that cibuildwheel will invoke directly. For example, if your build script invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your cross-build tools list.
 
 ## Tests
 
diff --git a/test/test_ios.py b/test/test_ios.py
index ca6bc7ab2..c3cfa61af 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -2,14 +2,15 @@
 
 import os
 import platform
+import shutil
 import subprocess
 
 import pytest
 
 from . import test_projects, utils
 
-basic_project = test_projects.new_c_project()
-basic_project.files["tests/test_platform.py"] = f"""
+basic_project_files = {
+    "tests/test_platform.py": f"""
 import platform
 from unittest import TestCase
 
@@ -18,6 +19,7 @@ def test_platform(self):
         self.assertEqual(platform.machine(), "{platform.machine()}")
 
 """
+}
 
 
 # iOS tests shouldn't be run in parallel, because they're dependent on calling
@@ -35,19 +37,41 @@ def test_platform(self):
         {"CIBW_PLATFORM": "ios", "CIBW_BUILD_FRONTEND": "build"},
     ],
 )
-def test_ios_platforms(tmp_path, build_config):
+def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
     if utils.platform != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
         pytest.skip("this test only works with Xcode 13.0 or greater")
 
+    # Create a temporary "bin" directory, symlink a tool that we know eixsts
+    # (/usr/bin/true) into that location under a name that should be unique,
+    # and add the temp bin directory to the PATH.
+    tools_dir = tmp_path / "bin"
+    tools_dir.mkdir()
+    tools_dir.joinpath("does-exist").symlink_to(shutil.which("true"))
+
+    monkeypatch.setenv("PATH", str(tools_dir), prepend=os.pathsep)
+
+    # Generate a test project that has an additional before-build step using the
+    # known-to-exist tool.
     project_dir = tmp_path / "project"
+    setup_py_add = "import subprocess\nsubprocess.run('does-exist', check=True)\n"
+    basic_project = test_projects.new_c_project(setup_py_add=setup_py_add)
+    basic_project.files.update(basic_project_files)
     basic_project.generate(project_dir)
 
+    # Build and test the wheels. Mark the "does-exist" tool as a cross-build
+    # tool, and invoke it during a `before-build` step. It will also be invoked
+    # when `setup.py` is invoked.
+    #
+    # Tests are only executed on simulator. The test suite passes if it's
+    # running on the same architecture as the current platform.
     actual_wheels = utils.cibuildwheel_run(
         project_dir,
         add_env={
+            "CIBW_BEFORE_BUILD": "does-exist",
             "CIBW_BUILD": "cp313-*",
+            "CIBW_XBUILD_TOOLS": "does-exist",
             "CIBW_TEST_SOURCES": "tests",
             "CIBW_TEST_COMMAND": "unittest discover tests test_platform.py",
             "CIBW_BUILD_VERBOSITY": "1",
@@ -55,11 +79,10 @@ def test_ios_platforms(tmp_path, build_config):
         },
     )
 
+    # The expected wheels were produced.
     ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_")
     platform_machine = platform.machine()
 
-    # Tests are only executed on simulator. The test suite passes if it's
-    # running on the same architecture as the current platform.
     if platform_machine == "x86_64":
         expected_wheels = {
             f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl",
@@ -73,15 +96,21 @@ def test_ios_platforms(tmp_path, build_config):
 
     assert set(actual_wheels) == expected_wheels
 
+    # The user was notified that the cross-build tool was found.
+    captured = capfd.readouterr()
+    assert "'does-exist' will be included in the cross-build environment" in captured.out
+
 
-@pytest.mark.serial
 def test_no_test_sources(tmp_path, capfd):
+    """Build will fail if test-sources isn't defined."""
     if utils.platform != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
         pytest.skip("this test only works with Xcode 13.0 or greater")
 
     project_dir = tmp_path / "project"
+    basic_project = test_projects.new_c_project()
+    basic_project.files.update(basic_project_files)
     basic_project.generate(project_dir)
 
     with pytest.raises(subprocess.CalledProcessError):
@@ -94,5 +123,125 @@ def test_no_test_sources(tmp_path, capfd):
             },
         )
 
+    # The error message indicates the configuration issue.
     captured = capfd.readouterr()
     assert "Testing on iOS requires a definition of test-sources." in captured.err
+
+
+def test_missing_xbuild_tool(tmp_path, capfd):
+    """Build will fail if xbuild-tools references a non-existent tool."""
+    if utils.platform != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+    basic_project = test_projects.new_c_project()
+    basic_project.files.update(basic_project_files)
+    basic_project.generate(project_dir)
+
+    with pytest.raises(subprocess.CalledProcessError):
+        utils.cibuildwheel_run(
+            project_dir,
+            add_env={
+                "CIBW_PLATFORM": "ios",
+                "CIBW_BUILD": "cp313-*",
+                "CIBW_TEST_COMMAND": "tests",
+                "CIBW_XBUILD_TOOLS": "does-not-exist",
+            },
+        )
+
+    # The error message indicates the problem tool.
+    captured = capfd.readouterr()
+    assert "Could not find a 'does-not-exist' executable on the path." in captured.err
+
+
+def test_no_xbuild_tool_definition(tmp_path, capfd):
+    """Build will succeed with a warning if there is no xbuild-tools definition."""
+    if utils.platform != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+    basic_project = test_projects.new_c_project()
+    basic_project.files.update(basic_project_files)
+    basic_project.generate(project_dir)
+
+    # Build, but don't test the wheels; we're only checking that the right
+    # warning was raised.
+    actual_wheels = utils.cibuildwheel_run(
+        project_dir,
+        add_env={
+            "CIBW_PLATFORM": "ios",
+            "CIBW_BUILD": "cp313-*",
+            "CIBW_TEST_SKIP": "*",
+        },
+    )
+
+    # The expected wheels were produced.
+    ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_")
+    platform_machine = platform.machine()
+
+    if platform_machine == "x86_64":
+        expected_wheels = {
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl",
+        }
+
+    elif platform_machine == "arm64":
+        expected_wheels = {
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl",
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl",
+        }
+
+    assert set(actual_wheels) == expected_wheels
+
+    # The user was notified that there was no cross-build tool definition.
+    captured = capfd.readouterr()
+    assert "Your project configuration does not define any cross-build tools." in captured.err
+
+
+def test_empty_xbuild_tool_definition(tmp_path, capfd):
+    """Build will succeed with no warning if there is an empty xbuild-tools definition."""
+    if utils.platform != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+    basic_project = test_projects.new_c_project()
+    basic_project.files.update(basic_project_files)
+    basic_project.generate(project_dir)
+
+    # Build, but don't test the wheels; we're only checking that a warning
+    # wasn't raised.
+    actual_wheels = utils.cibuildwheel_run(
+        project_dir,
+        add_env={
+            "CIBW_PLATFORM": "ios",
+            "CIBW_BUILD": "cp313-*",
+            "CIBW_TEST_SKIP": "*",
+            "CIBW_XBUILD_TOOLS": "",
+        },
+    )
+
+    # The expected wheels were produced.
+    ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_")
+    platform_machine = platform.machine()
+
+    if platform_machine == "x86_64":
+        expected_wheels = {
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl",
+        }
+
+    elif platform_machine == "arm64":
+        expected_wheels = {
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl",
+            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl",
+        }
+
+    assert set(actual_wheels) == expected_wheels
+
+    # The warnings about cross-build notifications were silenced.
+    captured = capfd.readouterr()
+    assert "Your project configuration does not define any cross-build tools." not in captured.err
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index bcf242916..fbf181754 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -618,3 +618,31 @@ def test_get_build_frontend_extra_flags_warning(
     )
     assert args == ["-Ca", "-Cb", "-1"]
     mock_warning.assert_called_once()
+
+
+@pytest.mark.parametrize(
+    ("definition", "expected"),
+    [
+        ("", None),
+        ("xbuild-tools = []", []),
+        ('xbuild-tools = ["cmake", "rustc"]', ["cmake", "rustc"]),
+    ],
+)
+def test_xbuild_tools_handling(tmp_path: Path, definition: str, expected: list[str] | None) -> None:
+    args = CommandLineArguments.defaults()
+    args.package_dir = tmp_path
+
+    pyproject_toml: Path = tmp_path / "pyproject.toml"
+    pyproject_toml.write_text(
+        textwrap.dedent(
+            f"""\
+            [tool.cibuildwheel]
+            {definition}
+            """
+        )
+    )
+
+    options = Options(platform="ios", command_line_arguments=args, env={})
+
+    local = options.build_options("cp313-ios_13_0_arm64_iphoneos")
+    assert local.xbuild_tools == expected
diff --git a/unit_test/options_toml_test.py b/unit_test/options_toml_test.py
index e5c8d5593..6ec634152 100644
--- a/unit_test/options_toml_test.py
+++ b/unit_test/options_toml_test.py
@@ -17,6 +17,7 @@
 [tool.cibuildwheel]
 build = "cp39*"
 environment = {THING = "OTHER", FOO="BAR"}
+xbuild-tools = ["first"]
 
 test-command = "pyproject"
 test-requires = "something"
@@ -89,6 +90,7 @@ def test_envvar_override(tmp_path, platform):
         env={
             "CIBW_BUILD": "cp38*",
             "CIBW_MANYLINUX_X86_64_IMAGE": "manylinux_2_24",
+            "CIBW_XBUILD_TOOLS": "cmake rustc",
             "CIBW_TEST_COMMAND": "mytest",
             "CIBW_TEST_REQUIRES": "docs",
             "CIBW_TEST_GROUPS": "mgroup two",
@@ -104,6 +106,10 @@ def test_envvar_override(tmp_path, platform):
     assert options_reader.get("manylinux-x86_64-image") == "manylinux_2_24"
     assert options_reader.get("manylinux-i686-image") == "manylinux2014"
 
+    assert (
+        options_reader.get("xbuild-tools", option_format=ListFormat(" ", quote=shlex.quote))
+        == "cmake rustc"
+    )
     assert (
         options_reader.get("test-sources", option_format=ListFormat(" ", quote=shlex.quote))
         == 'first "second third"'
@@ -269,6 +275,7 @@ def test_environment_override_empty(tmp_path):
         env={
             "CIBW_MANYLINUX_I686_IMAGE": "",
             "CIBW_MANYLINUX_AARCH64_IMAGE": "manylinux1",
+            "CIBW_XBUILD_TOOLS": "",
         },
     )
 
@@ -280,6 +287,10 @@ def test_environment_override_empty(tmp_path):
     assert options_reader.get("manylinux-i686-image", ignore_empty=True) == "manylinux1"
     assert options_reader.get("manylinux-aarch64-image", ignore_empty=True) == "manylinux1"
 
+    assert (
+        options_reader.get("xbuild-tools", option_format=ListFormat(" ", quote=shlex.quote)) == ""
+    )
+
 
 @pytest.mark.parametrize("ignore_empty", [True, False], ids=["ignore_empty", "no_ignore_empty"])
 def test_resolve_cascade(ignore_empty):

From 1172caba2bf7c51f3f10b5de37e39fd16a1201b5 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Tue, 8 Apr 2025 06:31:23 +0200
Subject: [PATCH 036/193] [Bot] Update dependencies (#2350)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |  6 +--
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python38.txt        |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 docs/working-examples.md                      |  4 +-
 10 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 7ae6131b1..aed02adf7 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -52,11 +52,11 @@ pip==25.0.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
 platformdirs==4.3.7
     # via virtualenv
-pydantic==2.11.1
+pydantic==2.11.2
     # via
     #   pyodide-build
     #   pyodide-lock
-pydantic-core==2.33.0
+pydantic-core==2.33.1
     # via pydantic
 pygments==2.19.1
     # via rich
@@ -92,7 +92,7 @@ typer==0.15.2
     #   auditwheel-emscripten
     #   pyodide-build
     #   pyodide-cli
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via
     #   anyio
     #   pydantic
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index b5668a46c..49a2b619f 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 9520878ef..524d961e2 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 9520878ef..524d961e2 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 9520878ef..524d961e2 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 274b1f2b6..9ea26b911 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index b5668a46c..49a2b619f 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 9520878ef..524d961e2 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.0
+typing-extensions==4.13.1
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index bd1f0b0a6..03e412bea 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,45 +1,45 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.30-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.30-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.30-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.05-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.05-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.05-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.05-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.30-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.05-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.05-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.30-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.30-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.30-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.05-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.05-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.05-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.05-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.30-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.30-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.03.30-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.05-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.05-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.05-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.05-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.30-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.30-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.03.30-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.05-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.05-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.05-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.05-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.30-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.30-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.05-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.05-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.05-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.05-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.30-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.30-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.03.30-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.05-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.05-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.05-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.30-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.30-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.05-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.05-1
 
diff --git a/docs/working-examples.md b/docs/working-examples.md
index ed5d48ddf..1db276593 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -85,9 +85,9 @@ title: Working examples
 | [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [Tokenizer][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
 | [iminuit][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
+| [Confluent client for Kafka][]    | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
 | [pillow-heif][]                   | ![github icon][] ![cirrusci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Bindings to libheif library with third party dependencies. Fully automated CI for tests and publishing including Apple Silicon builds. |
 | [keyvi][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime |
-| [Confluent client for Kafka][]    | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
 | [PyGLM][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python |
 | [TgCrypto][]                      | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. |
 | [iDynTree][]                      | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 |
@@ -195,9 +195,9 @@ title: Working examples
 [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [Tokenizer]: https://github.com/OpenNMT/Tokenizer
 [iminuit]: https://github.com/scikit-hep/iminuit
+[Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
 [pillow-heif]: https://github.com/bigcat88/pillow_heif
 [keyvi]: https://github.com/KeyviDev/keyvi
-[Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
 [PyGLM]: https://github.com/Zuzu-Typ/PyGLM
 [TgCrypto]: https://github.com/pyrogram/tgcrypto
 [iDynTree]: https://github.com/robotology/idyntree

From 08cbc6f8d0319db8fe5f75b7b7ee93691b5d58b1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 8 Apr 2025 06:31:44 +0200
Subject: [PATCH 037/193] chore(deps): bump actions/create-github-app-token
 from 1 to 2 in the actions group (#2351)

chore(deps): bump actions/create-github-app-token in the actions group

Bumps the actions group with 1 update: [actions/create-github-app-token](https://github.com/actions/create-github-app-token).


Updates `actions/create-github-app-token` from 1 to 2
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](https://github.com/actions/create-github-app-token/compare/v1...v2)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/update-dependencies.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml
index 9c14ed681..a31aaf887 100644
--- a/.github/workflows/update-dependencies.yml
+++ b/.github/workflows/update-dependencies.yml
@@ -23,7 +23,7 @@ jobs:
 
     # we use this step to grab a Github App auth token, so that PRs generated by this workflow
     # run the GHA tests.
-    - uses: actions/create-github-app-token@v1
+    - uses: actions/create-github-app-token@v2
       id: generate-token
       if: github.ref == 'refs/heads/main' && github.repository == 'pypa/cibuildwheel'
       with:

From 8e2cc78913537d7eeda31bb28b3506bc70b23532 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 8 Apr 2025 06:31:59 +0200
Subject: [PATCH 038/193] [pre-commit.ci] pre-commit autoupdate (#2352)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.2 → v0.11.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.2...v0.11.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 73e18b3eb..75e2789a0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.2
+  rev: v0.11.4
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 93314a8d65b1ffe709ee97eabc1494b10c17b721 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 9 Apr 2025 09:58:57 -0400
Subject: [PATCH 039/193] refactor: pull out dependency_constraint (#2347)

* refactor: pull out dependency_constraint

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* refactor: address feedback

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: better parallel

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 azure-pipelines.yml               |  4 ++--
 bin/run_tests.py                  |  6 +++++-
 cibuildwheel/platforms/ios.py     | 24 ++++++++++--------------
 cibuildwheel/platforms/macos.py   | 30 ++++++++++++++++--------------
 cibuildwheel/platforms/pyodide.py | 18 +++++++-----------
 cibuildwheel/platforms/windows.py | 24 +++++++++++-------------
 cibuildwheel/venv.py              | 22 ++++++++++++++--------
 7 files changed, 65 insertions(+), 63 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 76b1c6186..eaf93958e 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -7,7 +7,7 @@ pr:
 jobs:
 - job: linux_311
   timeoutInMinutes: 120
-  pool: {vmImage: 'Ubuntu-20.04'}
+  pool: {vmImage: 'Ubuntu-22.04'}
   steps:
     - task: UsePythonVersion@0
       inputs:
@@ -27,7 +27,7 @@ jobs:
     - bash: |
         python -m pip install dependency-groups
         python -m dependency_groups test | xargs python -m pip install -e.
-        python ./bin/run_tests.py --num-processes 2
+        python ./bin/run_tests.py
 
 - job: windows_311
   pool: {vmImage: 'windows-2019'}
diff --git a/bin/run_tests.py b/bin/run_tests.py
index bbfbc25a5..b807938b1 100755
--- a/bin/run_tests.py
+++ b/bin/run_tests.py
@@ -8,7 +8,11 @@
 from pathlib import Path
 
 if __name__ == "__main__":
-    default_cpu_count = os.cpu_count() or 2
+    if sys.version_info < (3, 13):
+        default_cpu_count = os.cpu_count() or 2
+    else:
+        default_cpu_count = os.process_cpu_count() or 2
+
     parser = argparse.ArgumentParser()
     parser.add_argument(
         "--run-podman", action="store_true", default=False, help="run podman tests (linux only)"
diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 06fce61fa..3473ef740 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -24,7 +24,6 @@
 from ..logger import log
 from ..options import Options
 from ..selector import BuildSelector
-from ..typing import PathOrStr
 from ..util import resources
 from ..util.cmd import call, shell
 from ..util.file import (
@@ -39,7 +38,7 @@
     find_compatible_wheel,
     get_pip_version,
 )
-from ..venv import virtualenv
+from ..venv import constraint_flags, virtualenv
 from .macos import install_cpython as install_build_cpython
 
 
@@ -151,7 +150,7 @@ def cross_virtualenv(
     multiarch: str,
     build_python: Path,
     venv_path: Path,
-    dependency_constraint_flags: Sequence[PathOrStr],
+    dependency_constraint: Path | None,
     xbuild_tools: Sequence[str] | None,
 ) -> dict[str, str]:
     """Create a cross-compilation virtual environment.
@@ -178,8 +177,8 @@ def cross_virtualenv(
     :param build_python: The path to the python binary for the build platform
     :param venv_path: The path where the cross virtual environment should be
         created.
-    :param dependency_constraint_flags: Any flags that should be used when
-        constraining dependencies in the environment.
+    :param dependency_constraint: A path to a constraint file that should be
+        used when constraining dependencies in the environment.
     :param xbuild_tools: A list of executable names (without paths) that are
         on the path, but must be preserved in the cross environment.
     """
@@ -188,7 +187,7 @@ def cross_virtualenv(
         py_version,
         build_python,
         venv_path,
-        dependency_constraint_flags,
+        dependency_constraint,
         use_uv=False,
     )
 
@@ -279,7 +278,7 @@ def setup_python(
     tmp: Path,
     *,
     python_configuration: PythonConfiguration,
-    dependency_constraint_flags: Sequence[PathOrStr],
+    dependency_constraint: Path | None,
     environment: ParsedEnvironment,
     build_frontend: BuildFrontendName,
     xbuild_tools: Sequence[str] | None,
@@ -334,7 +333,7 @@ def setup_python(
         multiarch=python_configuration.multiarch,
         build_python=build_python,
         venv_path=venv_path,
-        dependency_constraint_flags=dependency_constraint_flags,
+        dependency_constraint=dependency_constraint,
         xbuild_tools=xbuild_tools,
     )
     venv_bin_path = venv_path / "bin"
@@ -351,7 +350,7 @@ def setup_python(
         "install",
         "--upgrade",
         "pip",
-        *dependency_constraint_flags,
+        *constraint_flags(dependency_constraint),
         env=env,
         cwd=venv_path,
     )
@@ -397,7 +396,7 @@ def setup_python(
             "install",
             "--upgrade",
             "build[virtualenv]",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
         )
     else:
@@ -453,14 +452,11 @@ def build(options: Options, tmp_path: Path) -> None:
             constraints_path = build_options.dependency_constraints.get_for_python_version(
                 version=config.version, tmp_dir=identifier_tmp_dir
             )
-            dependency_constraint_flags: Sequence[PathOrStr] = (
-                ["-c", constraints_path] if constraints_path else []
-            )
 
             target_install_path, env = setup_python(
                 identifier_tmp_dir / "build",
                 python_configuration=config,
-                dependency_constraint_flags=dependency_constraint_flags,
+                dependency_constraint=constraints_path,
                 environment=build_options.environment,
                 build_frontend=build_frontend.name,
                 xbuild_tools=build_options.xbuild_tools,
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 4c75859f0..428971263 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -7,7 +7,7 @@
 import subprocess
 import sys
 import typing
-from collections.abc import Sequence, Set
+from collections.abc import Set
 from dataclasses import dataclass
 from pathlib import Path
 from typing import Literal, assert_never
@@ -23,7 +23,6 @@
 from ..logger import log
 from ..options import Options
 from ..selector import BuildSelector
-from ..typing import PathOrStr
 from ..util import resources
 from ..util.cmd import call, shell
 from ..util.file import (
@@ -34,7 +33,7 @@
 )
 from ..util.helpers import prepare_command, unwrap
 from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
-from ..venv import find_uv, virtualenv
+from ..venv import constraint_flags, find_uv, virtualenv
 
 
 @functools.cache
@@ -195,7 +194,7 @@ def install_pypy(tmp: Path, url: str) -> Path:
 def setup_python(
     tmp: Path,
     python_configuration: PythonConfiguration,
-    dependency_constraint_flags: Sequence[PathOrStr],
+    dependency_constraint: Path | None,
     environment: ParsedEnvironment,
     build_frontend: BuildFrontendName,
 ) -> tuple[Path, dict[str, str]]:
@@ -226,7 +225,7 @@ def setup_python(
         python_configuration.version,
         base_python,
         venv_path,
-        dependency_constraint_flags,
+        dependency_constraint,
         use_uv=use_uv,
     )
     venv_bin_path = venv_path / "bin"
@@ -256,7 +255,7 @@ def setup_python(
             "install",
             "--upgrade",
             "pip",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
             cwd=venv_path,
         )
@@ -351,7 +350,7 @@ def setup_python(
             "install",
             "--upgrade",
             "delocate",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
         )
     elif build_frontend == "build":
@@ -361,7 +360,7 @@ def setup_python(
             "--upgrade",
             "delocate",
             "build[virtualenv]",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
         )
     elif build_frontend == "build[uv]":
@@ -373,7 +372,7 @@ def setup_python(
             "--upgrade",
             "delocate",
             "build[virtualenv, uv]",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
         )
     else:
@@ -427,14 +426,11 @@ def build(options: Options, tmp_path: Path) -> None:
             constraints_path = build_options.dependency_constraints.get_for_python_version(
                 version=config.version, tmp_dir=identifier_tmp_dir
             )
-            dependency_constraint_flags: Sequence[PathOrStr] = (
-                ["-c", constraints_path] if constraints_path else []
-            )
 
             base_python, env = setup_python(
                 identifier_tmp_dir / "build",
                 config,
-                dependency_constraint_flags,
+                constraints_path,
                 build_options.environment,
                 build_frontend.name,
             )
@@ -621,7 +617,13 @@ def build(options: Options, tmp_path: Path) -> None:
                     # set up a virtual environment to install and test from, to make sure
                     # there are no dependencies that were pulled in at build time.
                     if not use_uv:
-                        call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
+                        call(
+                            "pip",
+                            "install",
+                            "virtualenv",
+                            *constraint_flags(constraints_path),
+                            env=env,
+                        )
 
                     venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
 
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index ca9edcb20..1c05dd0cd 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -3,7 +3,7 @@
 import shutil
 import sys
 import tomllib
-from collections.abc import Sequence, Set
+from collections.abc import Set
 from dataclasses import dataclass
 from pathlib import Path
 from tempfile import TemporaryDirectory
@@ -18,7 +18,6 @@
 from ..logger import log
 from ..options import Options
 from ..selector import BuildSelector
-from ..typing import PathOrStr
 from ..util import resources
 from ..util.cmd import call, shell
 from ..util.file import (
@@ -31,7 +30,7 @@
 )
 from ..util.helpers import prepare_command
 from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
-from ..venv import virtualenv
+from ..venv import constraint_flags, virtualenv
 
 IS_WIN: Final[bool] = sys.platform.startswith("win")
 
@@ -145,14 +144,14 @@ def get_base_python(identifier: str) -> Path:
 def setup_python(
     tmp: Path,
     python_configuration: PythonConfiguration,
-    dependency_constraint_flags: Sequence[PathOrStr],
+    constraints_path: Path | None,
     environment: ParsedEnvironment,
 ) -> dict[str, str]:
     base_python = get_base_python(python_configuration.identifier)
 
     log.step("Setting up build environment...")
     venv_path = tmp / "venv"
-    env = virtualenv(python_configuration.version, base_python, venv_path, [], use_uv=False)
+    env = virtualenv(python_configuration.version, base_python, venv_path, None, use_uv=False)
     venv_bin_path = venv_path / "bin"
     assert venv_bin_path.exists()
     env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
@@ -166,7 +165,7 @@ def setup_python(
         "install",
         "--upgrade",
         "pip",
-        *dependency_constraint_flags,
+        *constraint_flags(constraints_path),
         env=env,
         cwd=venv_path,
     )
@@ -198,7 +197,7 @@ def setup_python(
         "auditwheel-emscripten",
         "build[virtualenv]",
         "pyodide-build",
-        *dependency_constraint_flags,
+        *constraint_flags(constraints_path),
         env=env,
     )
 
@@ -269,14 +268,11 @@ def build(options: Options, tmp_path: Path) -> None:
             constraints_path = build_options.dependency_constraints.get_for_python_version(
                 version=config.version, variant="pyodide", tmp_dir=identifier_tmp_dir
             )
-            dependency_constraint_flags: Sequence[PathOrStr] = (
-                ["-c", constraints_path] if constraints_path else []
-            )
 
             env = setup_python(
                 identifier_tmp_dir / "build",
                 config,
-                dependency_constraint_flags,
+                constraints_path,
                 build_options.environment,
             )
             pip_version = get_pip_version(env)
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 6d56236a2..e429015b9 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -3,7 +3,7 @@
 import shutil
 import subprocess
 import textwrap
-from collections.abc import MutableMapping, Sequence, Set
+from collections.abc import MutableMapping, Set
 from dataclasses import dataclass
 from functools import cache
 from pathlib import Path
@@ -18,13 +18,12 @@
 from ..logger import log
 from ..options import Options
 from ..selector import BuildSelector
-from ..typing import PathOrStr
 from ..util import resources
 from ..util.cmd import call, shell
 from ..util.file import CIBW_CACHE_PATH, copy_test_sources, download, extract_zip, move_file
 from ..util.helpers import prepare_command, unwrap
 from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
-from ..venv import find_uv, virtualenv
+from ..venv import constraint_flags, find_uv, virtualenv
 
 
 def get_nuget_args(
@@ -219,7 +218,7 @@ def can_use_uv(python_configuration: PythonConfiguration) -> bool:
 def setup_python(
     tmp: Path,
     python_configuration: PythonConfiguration,
-    dependency_constraint_flags: Sequence[PathOrStr],
+    dependency_constraint: Path | None,
     environment: ParsedEnvironment,
     build_frontend: BuildFrontendName,
 ) -> tuple[Path, dict[str, str]]:
@@ -257,7 +256,7 @@ def setup_python(
         python_configuration.version,
         base_python,
         venv_path,
-        dependency_constraint_flags,
+        dependency_constraint,
         use_uv=use_uv,
     )
 
@@ -276,7 +275,7 @@ def setup_python(
             "install",
             "--upgrade",
             "pip",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
             cwd=venv_path,
         )
@@ -310,7 +309,7 @@ def setup_python(
             "install",
             "--upgrade",
             "build[virtualenv]",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
         )
     elif build_frontend == "build[uv]":
@@ -321,7 +320,7 @@ def setup_python(
             "install",
             "--upgrade",
             "build[virtualenv]",
-            *dependency_constraint_flags,
+            *constraint_flags(dependency_constraint),
             env=env,
         )
 
@@ -370,15 +369,12 @@ def build(options: Options, tmp_path: Path) -> None:
                 version=config.version,
                 tmp_dir=identifier_tmp_dir,
             )
-            dependency_constraint_flags: Sequence[PathOrStr] = (
-                ["-c", constraints_path] if constraints_path else []
-            )
 
             # install Python
             base_python, env = setup_python(
                 identifier_tmp_dir / "build",
                 config,
-                dependency_constraint_flags,
+                constraints_path,
                 build_options.environment,
                 build_frontend.name,
             )
@@ -491,7 +487,9 @@ def build(options: Options, tmp_path: Path) -> None:
                 # set up a virtual environment to install and test from, to make sure
                 # there are no dependencies that were pulled in at build time.
                 if not use_uv:
-                    call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
+                    call(
+                        "pip", "install", "virtualenv", *constraint_flags(constraints_path), env=env
+                    )
 
                 venv_dir = identifier_tmp_dir / "venv-test"
 
diff --git a/cibuildwheel/venv.py b/cibuildwheel/venv.py
index 6f7ca400a..b8e9cb789 100644
--- a/cibuildwheel/venv.py
+++ b/cibuildwheel/venv.py
@@ -12,7 +12,6 @@
 from packaging.requirements import InvalidRequirement, Requirement
 from packaging.version import Version
 
-from .typing import PathOrStr
 from .util import resources
 from .util.cmd import call
 from .util.file import CIBW_CACHE_PATH, download
@@ -36,8 +35,18 @@ def _ensure_virtualenv(version: str) -> Path:
     return path
 
 
+def constraint_flags(
+    dependency_constraint: Path | None,
+) -> Sequence[str]:
+    """
+    Returns the flags to pass to pip for the given dependency constraint.
+    """
+
+    return ["-c", dependency_constraint.as_uri()] if dependency_constraint else []
+
+
 def _parse_pip_constraint_for_virtualenv(
-    dependency_constraint_flags: Sequence[PathOrStr],
+    constraint_path: Path | None,
 ) -> str:
     """
     Parses the constraints file referenced by `dependency_constraint_flags` and returns a dict where
@@ -48,10 +57,7 @@ def _parse_pip_constraint_for_virtualenv(
     If it can't get an exact version, the real constraint will be handled by the
     {macos|windows}.setup_python function.
     """
-    assert len(dependency_constraint_flags) in {0, 2}
-    if len(dependency_constraint_flags) == 2:
-        assert dependency_constraint_flags[0] == "-c"
-        constraint_path = Path(dependency_constraint_flags[1])
+    if constraint_path:
         assert constraint_path.exists()
         with constraint_path.open(encoding="utf-8") as constraint_file:
             for line_ in constraint_file:
@@ -84,7 +90,7 @@ def virtualenv(
     version: str,
     python: Path,
     venv_path: Path,
-    dependency_constraint_flags: Sequence[PathOrStr],
+    dependency_constraint: Path | None,
     *,
     use_uv: bool,
 ) -> dict[str, str]:
@@ -103,7 +109,7 @@ def virtualenv(
         call("uv", "venv", venv_path, "--python", python)
     else:
         virtualenv_app = _ensure_virtualenv(version)
-        pip_constraint = _parse_pip_constraint_for_virtualenv(dependency_constraint_flags)
+        pip_constraint = _parse_pip_constraint_for_virtualenv(dependency_constraint)
         additional_flags = [f"--pip={pip_constraint}", "--no-setuptools", "--no-wheel"]
 
         # Using symlinks to pre-installed seed packages is really the fastest way to get a virtual

From 74a7d8a68e3add1da8428b155e500a82dc1a412e Mon Sep 17 00:00:00 2001
From: Dimitri Papadopoulos Orfanos
 <3234522+DimitriPapadopoulos@users.noreply.github.com>
Date: Sun, 13 Apr 2025 07:06:33 +0200
Subject: [PATCH 040/193] chore: add a selection of PERF rules (#2313)

---
 cibuildwheel/options.py |  3 +--
 pyproject.toml          |  1 +
 test/utils.py           | 44 +++++++++++++++++++----------------------
 3 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 1e07a3f97..b0177e0c9 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -221,8 +221,7 @@ def format_table(self, table: SettingTable) -> str:
             if isinstance(v, str):
                 assignments.append((k, v))
             elif isinstance(v, Sequence):
-                for inner_v in v:
-                    assignments.append((k, str(inner_v)))
+                assignments.extend((k, str(inner_v)) for inner_v in v)
             else:
                 assignments.append((k, str(v)))
 
diff --git a/pyproject.toml b/pyproject.toml
index e3efab7ca..b3523e2be 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -201,6 +201,7 @@ extend-select = [
   "YTT",         # flake8-2020
   "EXE",         # flake8-executable
   "PYI",         # flake8-pyi
+  "PERF101", "PERF102", "PERF401", "PERF402", "PERF403",  # A selection of perflint codes
 ]
 ignore = [
   "PLR",     # Design related pylint codes
diff --git a/test/utils.py b/test/utils.py
index 02659fb6e..d999e1b40 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -8,7 +8,7 @@
 import platform as pm
 import subprocess
 import sys
-from collections.abc import Mapping, Sequence
+from collections.abc import Generator, Mapping, Sequence
 from pathlib import Path
 from tempfile import TemporaryDirectory
 from typing import Any, Final
@@ -165,7 +165,7 @@ def expected_wheels(
     single_arch: bool = False,
 ) -> list[str]:
     """
-    Returns a list of expected wheels from a run of cibuildwheel.
+    Returns the expected wheels from a run of cibuildwheel.
     """
     if machine_arch is None:
         machine_arch = pm.machine()
@@ -186,22 +186,21 @@ def expected_wheels(
         elif platform == "windows" and machine_arch == "AMD64":
             architectures.append("x86")
 
-    wheels: list[str] = []
-    for architecture in architectures:
-        wheels.extend(
-            _expected_wheels(
-                package_name,
-                package_version,
-                architecture,
-                manylinux_versions,
-                musllinux_versions,
-                macosx_deployment_target,
-                python_abi_tags,
-                include_universal2,
-                single_python,
-            )
+    return [
+        wheel
+        for architecture in architectures
+        for wheel in _expected_wheels(
+            package_name,
+            package_version,
+            architecture,
+            manylinux_versions,
+            musllinux_versions,
+            macosx_deployment_target,
+            python_abi_tags,
+            include_universal2,
+            single_python,
         )
-    return wheels
+    ]
 
 
 def _expected_wheels(
@@ -214,7 +213,7 @@ def _expected_wheels(
     python_abi_tags: list[str] | None,
     include_universal2: bool,
     single_python: bool,
-) -> list[str]:
+) -> Generator[str, None, None]:
     """
     Returns a list of expected wheels from a run of cibuildwheel.
     """
@@ -264,13 +263,12 @@ def _expected_wheels(
             )
         ]
 
-    wheels = []
-
     if platform == "pyodide":
         assert len(python_abi_tags) == 1
         python_abi_tag = python_abi_tags[0]
         platform_tag = "pyodide_2024_0_wasm32"
-        return [f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl"]
+        yield f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl"
+        return
 
     for python_abi_tag in python_abi_tags:
         platform_tags = []
@@ -321,9 +319,7 @@ def _expected_wheels(
             raise Exception(msg)
 
         for platform_tag in platform_tags:
-            wheels.append(f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl")
-
-    return wheels
+            yield f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl"
 
 
 def get_macos_version() -> tuple[int, int]:

From 1d34f85c633c3a645ed2612eca7b39ca6c529879 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sun, 13 Apr 2025 07:16:55 +0100
Subject: [PATCH 041/193] General docs improvements for v3.0 (#2280)

* Split the docs into sections

* Split the options page into two pages - so the reference is more clean

* Reorder/Tidy up this 'deliver' page to make auto-deploy more obvious

* Fix up some links

* Merge the platform docs into a single page to clean up the navbar.

* Split the setup page to make a page dedicated to CI setup

* Move general platform-specific information into the platforms page

* Tidy up the platforms page

* Remove some redundancy in the cpp/platforms docs

* Clarify this section

* Move another FAQ entry to the platforms page

* Remove some out-of-date entries from the FAQ

* A few corrections to the options page

* Fix some broken links

* Improve the Docker references

* Update bump version filename

* Improve options TOC information density

* whitespace

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>

* Convert the normal test invocation to `pytest ./tests`, not placeholder

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
---
 bin/bump_version.py       |   1 +
 docs/ci-services.md       | 185 ++++++++++++++++++++++
 docs/configuration.md     | 238 ++++++++++++++++++++++++++++
 docs/cpp_standards.md     |   3 +-
 docs/deliver-to-pypi.md   |  63 ++++----
 docs/extra.css            |  57 +------
 docs/extra.js             |  51 ++++++
 docs/faq.md               | 100 +-----------
 docs/options.md           | 320 +++++---------------------------------
 docs/platforms.md         | 236 ++++++++++++++++++++++++++++
 docs/platforms/ios.md     |  70 ---------
 docs/platforms/linux.md   |  13 --
 docs/platforms/macos.md   |  52 -------
 docs/platforms/pyodide.md |  13 --
 docs/platforms/windows.md |  15 --
 docs/setup.md             | 215 ++-----------------------
 mkdocs.yml                |  18 +--
 test/test_abi_variants.py |   2 +-
 test/test_before_test.py  |   4 +-
 test/test_emulation.py    |   2 +-
 test/test_testing.py      |  14 +-
 21 files changed, 817 insertions(+), 855 deletions(-)
 create mode 100644 docs/ci-services.md
 create mode 100644 docs/configuration.md
 create mode 100644 docs/platforms.md
 delete mode 100644 docs/platforms/ios.md
 delete mode 100644 docs/platforms/linux.md
 delete mode 100644 docs/platforms/macos.md
 delete mode 100644 docs/platforms/pyodide.md
 delete mode 100644 docs/platforms/windows.md

diff --git a/bin/bump_version.py b/bin/bump_version.py
index 41895a192..9e1a3592d 100755
--- a/bin/bump_version.py
+++ b/bin/bump_version.py
@@ -23,6 +23,7 @@
     ("docs/faq.md", "cibuildwheel=={}"),
     ("docs/faq.md", "cibuildwheel@v{}"),
     ("docs/setup.md", "cibuildwheel=={}"),
+    ("docs/ci-services.md", "cibuildwheel=={}"),
     ("examples/*", "cibuildwheel=={}"),
     ("examples/*", "cibuildwheel@v{}"),
 ]
diff --git a/docs/ci-services.md b/docs/ci-services.md
new file mode 100644
index 000000000..ba0c53ec8
--- /dev/null
+++ b/docs/ci-services.md
@@ -0,0 +1,185 @@
+---
+title: Configuring a CI service
+---
+
+## Configuring a CI service
+
+cibuildwheel works on many popular CI services. Others may work, but it will depend on the software installed on the CI machine/image. See the [platforms page](platforms.md) for details.
+
+### GitHub Actions [linux/mac/windows] {: #github-actions}
+
+To build Linux, macOS, and Windows wheels using GitHub Actions, create a `.github/workflows/build_wheels.yml` file in your repo.
+
+!!! tab "Action"
+    For GitHub Actions, `cibuildwheel` provides an action you can use. This is
+    concise and enables easier auto updating via GitHub's Dependabot; see
+    [Automatic updates](faq.md#automatic-updates).
+
+    > .github/workflows/build_wheels.yml
+
+    ```yaml
+    {% include "../examples/github-minimal.yml" %}
+    ```
+
+    Use `env:` to pass [build options](options.md) and `with:` to set
+    `package-dir: .`, `output-dir: wheelhouse` and `config-file: ''`
+    locations (those values are the defaults).
+
+!!! tab "pipx"
+    The GitHub Actions runners have pipx installed, so you can easily build in
+    just one line. This is internally how the action works; the main benefit of
+    the action form is easy updates via GitHub's Dependabot.
+
+    > .github/workflows/build_wheels.yml
+
+    ```yaml
+    {% include "../examples/github-pipx.yml" %}
+    ```
+
+!!! tab "Generic"
+    This is the most generic form using setup-python and pip; it looks the most
+    like the other CI examples. If you want to avoid having setup that takes
+    advantage of GitHub Actions features or pipx being preinstalled, this might
+    appeal to you.
+
+    > .github/workflows/build_wheels.yml
+    {%
+       include-markdown "../README.md"
+       start="<!--generic-github-start-->"
+       end="<!--generic-github-end-->"
+    %}
+
+Commit this file, and push to GitHub - either to your default branch, or to a PR branch. The build should start automatically.
+
+For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
+
+[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example to include iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI.
+
+
+### Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
+
+To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
+
+> azure-pipelines.yml
+
+```yaml
+{% include "../examples/azure-pipelines-minimal.yml" %}
+```
+
+Commit this file, enable building of your repo on Azure Pipelines, and push.
+
+Wheels will be stored for you and available through the Pipelines interface. For more info on this file, check out the [docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema).
+
+### Travis CI [linux/windows] {: #travis-ci}
+
+To build Linux and Windows wheels on Travis CI, create a `.travis.yml` file in your repo.
+
+> .travis.yml
+
+```yaml
+{% include "../examples/travis-ci-minimal.yml" %}
+```
+
+Commit this file, enable building of your repo on Travis CI, and push.
+
+Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](deliver-to-pypi.md). For more info on `.travis.yml`, check out the [docs](https://docs.travis-ci.com/).
+
+[`examples/travis-ci-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/travis-ci-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
+
+### AppVeyor [linux/mac/windows] {: #appveyor}
+
+To build Linux, Mac, and Windows wheels on AppVeyor, create an `appveyor.yml` file in your repo.
+
+> appveyor.yml
+
+```yaml
+{% include "../examples/appveyor-minimal.yml" %}
+```
+
+Commit this file, enable building of your repo on AppVeyor, and push.
+
+AppVeyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [AppVeyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](deliver-to-pypi.md) below.
+
+For more info on this config file, check out the [docs](https://www.appveyor.com/docs/).
+
+### CircleCI [linux/mac] {: #circleci}
+
+To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file in your repo,
+
+> .circleci/config.yml
+
+```yaml
+{% include "../examples/circleci-minimal.yml" %}
+```
+
+Commit this file, enable building of your repo on CircleCI, and push.
+
+!!! note
+    CircleCI doesn't enable free macOS containers for open source by default, but you can ask for access. See [here](https://circleci.com/docs/2.0/oss/#overview) for more information.
+
+CircleCI will store the built wheels for you - you can access them from the project console. Check out the CircleCI [docs](https://circleci.com/docs/2.0/configuration-reference/#section=configuration) for more info on this config file.
+
+### Gitlab CI [linux] {: #gitlab-ci}
+
+To build Linux wheels on Gitlab CI, create a `.gitlab-ci.yml` file in your repo,
+
+> .gitlab-ci.yml
+
+```yaml
+{% include "../examples/gitlab-minimal.yml" %}
+```
+
+Commit this file, and push to Gitlab. The pipeline should start automatically.
+
+Gitlab will store the built wheels for you - you can access them from the Pipelines view. Check out the Gitlab [docs](https://docs.gitlab.com/ee/ci/yaml/) for more info on this config file.
+
+### Cirrus CI [linux/mac/windows] {: #cirrus-ci}
+
+To build Linux, Mac, and Windows wheels on Cirrus CI, create a `.cirrus.yml` file in your repo,
+
+> .cirrus.yml
+
+```yaml
+{% include "../examples/cirrus-ci-minimal.yml" %}
+```
+
+Commit this file, enable building of your repo on Cirrus CI, and push.
+
+Cirrus CI will store the built wheels for you - you can access them from the individual task view. Check out the Cirrus CI [docs](https://cirrus-ci.org/guide/writing-tasks/) for more info on this config file.
+
+> ⚠️ Got an error? Check the [FAQ](faq.md).
+
+## Next steps
+
+Once you've got the wheel building successfully, you might want to set up [testing](options.md#test-command) or [automatic releases to PyPI](deliver-to-pypi.md#automatic-method).
+
+<script>
+  document.addEventListener('DOMContentLoaded', function() {
+    $('.toctree-l2>a, .rst-content h3').each(function(i, el) {
+      var text = $(el).text()
+      var match = text.match(/(.*) \[([a-z/]+)\]/);
+
+      if (match) {
+        var iconHTML = $.map(match[2].split('/'), function(ident) {
+          switch (ident) {
+            case 'linux':
+              return '<i class="fa fa-linux" aria-hidden="true"></i>'
+            case 'windows':
+              return '<i class="fa fa-windows" aria-hidden="true"></i>'
+            case 'mac':
+              return '<i class="fa fa-apple" aria-hidden="true"></i>'
+          }
+        }).join(' ');
+
+        $(el).append(
+          $('<div>')
+            .append(iconHTML)
+            .css({float: 'right'})
+        )
+        $(el).contents()
+          .filter(function(){ return this.nodeType == 3; }).first()
+          .replaceWith(match[1]);
+      }
+    });
+  });
+</script>
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 000000000..0a0ce985f
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,238 @@
+# Configuration methods
+
+cibuildwheel can either be configured using environment variables, or from
+config file such as `pyproject.toml`.
+
+This page describes how to set options. For a full list of available options, see the [options reference](options.md).
+
+## Environment variables {: #environment-variables}
+
+Environment variables can be set in your CI config. For example, to configure
+cibuildwheel to run tests, add the following YAML to your CI config file:
+
+!!! tab "GitHub Actions"
+
+    > .github/workflows/*.yml ([docs](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables)) (can be global, in job, or in step)
+
+    ```yaml
+    env:
+      CIBW_TEST_REQUIRES: pytest
+      CIBW_TEST_COMMAND: "pytest ./tests"
+    ```
+
+!!! tab "Azure Pipelines"
+
+    > azure-pipelines.yml ([docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables))
+
+    ```yaml
+    variables:
+      CIBW_TEST_REQUIRES: pytest
+      CIBW_TEST_COMMAND: "pytest ./tests"
+    ```
+
+!!! tab "Travis CI"
+
+    > .travis.yml ([docs](https://docs.travis-ci.com/user/environment-variables/))
+
+    ```yaml
+    env:
+      global:
+        - CIBW_TEST_REQUIRES=pytest
+        - CIBW_TEST_COMMAND="pytest ./tests"
+    ```
+
+!!! tab "AppVeyor"
+
+    > appveyor.yml ([docs](https://www.appveyor.com/docs/build-configuration/#environment-variables))
+
+    ```yaml
+    environment:
+      global:
+        CIBW_TEST_REQUIRES: pytest
+        CIBW_TEST_COMMAND: "pytest {project}\\tests"
+    ```
+
+!!! tab "CircleCI"
+
+    > .circleci/config.yml ([docs](https://circleci.com/docs/2.0/configuration-reference/#environment))
+
+    ```yaml
+    jobs:
+      job_name:
+        environment:
+          CIBW_TEST_REQUIRES: pytest
+          CIBW_TEST_COMMAND: "pytest ./tests"
+    ```
+
+!!! tab "Gitlab CI"
+
+    > .gitlab-ci.yml ([docs](https://docs.gitlab.com/ee/ci/variables/README.html#create-a-custom-variable-in-gitlab-ciyml))
+
+    ```yaml
+    linux:
+      variables:
+        CIBW_TEST_REQUIRES: pytest
+        CIBW_TEST_COMMAND: "pytest ./tests"
+    ```
+
+!!! tab "Cirrus CI"
+
+    > .cirrus.yml ([docs](https://cirrus-ci.org/guide/writing-tasks/#environment-variables))
+
+    ```yaml
+    env:
+      CIBW_TEST_REQUIRES: pytest
+      CIBW_TEST_COMMAND: "pytest ./tests"
+    ```
+
+## Configuration file {: #configuration-file}
+
+You can configure cibuildwheel with a config file, such as `pyproject.toml`.
+Options have the same names as the environment variable overrides, but are
+placed in `[tool.cibuildwheel]` and are lower case, with dashes, following
+common [TOML][https://toml.io] practice. Anything placed in subsections `linux`, `windows`,
+`macos`, or `pyodide` will only affect those platforms. Lists can be used
+instead of strings for items that are naturally a list. Multiline strings also
+work just like in the environment variables. Environment variables will take
+precedence if defined.
+
+The example above using environment variables could have been written like this:
+
+```toml
+[tool.cibuildwheel]
+test-requires = "pytest"
+test-command = "pytest ./tests"
+```
+
+The complete set of defaults for the current version of cibuildwheel are shown below:
+
+```toml
+{% include "../cibuildwheel/resources/defaults.toml" %}
+```
+
+
+!!! tip
+    Static configuration works across all CI systems, and can be used locally if
+    you run `cibuildwheel --platform linux`. This is preferred, but environment
+    variables are better if you need to change per-matrix element
+    (`CIBW_BUILD` is often in this category, for example), or if you cannot or do
+    not want to change a `pyproject.toml` file. You can specify a different file to
+    use with `--config-file` on the command line, as well.
+
+## Configuration overrides {: #overrides }
+
+One feature specific to the configuration files is the ability to override
+settings based on selectors. To use, add a ``tool.cibuildwheel.overrides``
+array, and specify a ``select`` string. Then any options you set will only
+apply to items that match that selector. These are applied in order, with later
+matches overriding earlier ones if multiple selectors match. Environment
+variables always override static configuration.
+
+A few of the options below have special handling in overrides. A different
+`before-all` will trigger a new container to launch on Linux, and cannot be
+overridden on macOS or Windows.  Overriding the image on linux will also
+trigger new containers, one per image. Some commands are not supported;
+`output-dir`, build/skip/test_skip selectors, and architectures cannot be
+overridden.
+
+You can specify a table of overrides in `inherit={}`, any list or table in this
+list will inherit from previous overrides or the main configuration. The valid
+options are `"none"` (the default), `"append"`, and `"prepend"`.
+
+#### Examples:
+
+```toml
+[tool.cibuildwheel.linux]
+before-all = "yum install mylib"
+test-command = "echo 'installed'"
+
+[[tool.cibuildwheel.overrides]]
+select = "*-musllinux*"
+before-all = "apk add mylib"
+```
+
+This example will override the before-all command on musllinux only, but will
+still run the test-command. Note the double brackets, this is an array in TOML,
+which means it can be given multiple times.
+
+```toml
+[tool.cibuildwheel]
+# Normal options, etc.
+manylinux-x86_64-image = "manylinux_2_34"
+
+[[tool.cibuildwheel.overrides]]
+select = "cp38-*"
+manylinux-x86_64-image = "manylinux2014"
+
+[[tool.cibuildwheel.overrides]]
+select = "cp3{9,10}-*"
+manylinux-x86_64-image = "manylinux_2_28"
+```
+
+This example will build CPython 3.8 wheels on manylinux2014, CPython 3.9-3.10
+wheels on manylinux_2_28, and manylinux_2_34 wheels for any newer Python
+(like 3.10).
+
+```toml
+[tool.cibuildwheel]
+environment = {FOO="BAR", "HAM"="EGGS"}
+test-command = ["pyproject"]
+
+[[tool.cibuildwheel.overrides]]
+select = "cp311*"
+
+inherit.test-command = "prepend"
+test-command = ["pyproject-before"]
+
+inherit.environment="append"
+environment = {FOO="BAZ", "PYTHON"="MONTY"}
+
+[[tool.cibuildwheel.overrides]]
+select = "cp311*"
+inherit.test-command = "append"
+test-command = ["pyproject-after"]
+```
+
+This example will provide the command `"pyproject-before && pyproject && pyproject-after"`
+on Python 3.11, and will have `environment = {FOO="BAZ", "PYTHON"="MONTY", "HAM"="EGGS"}`.
+
+
+## Extending existing options {: #inherit }
+
+In the TOML configuration, you can choose how tables and lists are inherited.
+By default, all values are overridden completely (`"none"`) but sometimes you'd
+rather `"append"` or `"prepend"` to an existing list or table. You can do this
+with the `inherit` table in overrides.  For example, if you want to add an environment
+variable for CPython 3.11, without `inherit` you'd have to repeat all the
+original environment variables in the override. With `inherit`, it's just:
+
+```toml
+[[tool.cibuildwheel.overrides]]
+select = "cp311*"
+inherit.environment = "append"
+environment.NEWVAR = "Added!"
+```
+
+For a table, `"append"` will replace a key if it exists, while `"prepend"` will
+only add a new key, older keys take precedence.
+
+Lists are also supported (and keep in mind that commands are lists). For
+example, you can print a message before and after a wheel is repaired:
+
+```toml
+[[tool.cibuildwheel.overrides]]
+select = "*"
+inherit.repair-wheel-command = "prepend"
+repair-wheel-command = "echo 'Before repair'"
+
+[[tool.cibuildwheel.overrides]]
+select = "*"
+inherit.repair-wheel-command = "append"
+repair-wheel-command = "echo 'After repair'"
+```
+
+As seen in this example, you can have multiple overrides match - they match top
+to bottom, with the config being accumulated. If you need platform-specific
+inheritance, you can use `select = "*-????linux_*"` for Linux, `select =
+"*-win_*"` for Windows, and `select = "*-macosx_*"` for macOS. As always,
+environment variables will completely override any TOML configuration.
diff --git a/docs/cpp_standards.md b/docs/cpp_standards.md
index 3b49481ee..b22b2ff72 100644
--- a/docs/cpp_standards.md
+++ b/docs/cpp_standards.md
@@ -15,11 +15,10 @@ The past end-of-life `manylinux2014` image (based on CentOS 7) contains a versio
 
 ## macOS and deployment target versions
 
-OS X/macOS allows you to specify a so-called "deployment target" version that will ensure backwards compatibility with older versions of macOS. One way to do this is by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable.
+The [`MACOSX_DEPLOYMENT_TARGET` environment variable](platforms.md#macos-version-compatibility) is used to set the minimum deployment target for macOS.
 
 However, to enable modern C++ standards, the deployment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
 
-
 To get C++17 support, Xcode 9.3+ is needed, requiring at least macOS 10.13 on the build machine. To use C++17 library features and link against the C++ runtime library, set `MACOSX_DEPLOYMENT_TARGET` to `"10.13"` or `"10.14"` (or higher) - macOS 10.13 offers partial C++17 support (e.g., the filesystem header is in experimental, offering `#include <experimental/filesystem>` instead of `#include <filesystem>`); macOS 10.14 has full C++17 support. CPython 3.12+ require 10.13+ anyway.
 
 However, if only C++17 compiler and standard template library (STL) features are used (not needing a C++17 runtime) it might be possible to set `MACOSX_DEPLOYMENT_TARGET` to a lower value, such as `"10.9"`. To find out if this is the case, try compiling and running with a lower `MACOSX_DEPLOYMENT_TARGET`: if C++17 features are used that require a more recent deployment target, building the wheel should fail.
diff --git a/docs/deliver-to-pypi.md b/docs/deliver-to-pypi.md
index d03e2d6f9..6453ff6f8 100644
--- a/docs/deliver-to-pypi.md
+++ b/docs/deliver-to-pypi.md
@@ -6,42 +6,23 @@ title: Delivering to PyPI
 
 After you've built your wheels, you'll probably want to deliver them to PyPI.
 
-## Manual method
-
-On your development machine, install [pipx](https://pypa.github.io/pipx/) and do the following:
-
-```bash
-# Either download the SDist from your CI, or make it:
-# Clear out your 'dist' folder.
-rm -rf dist
-# Make a source distribution
-pipx run build --sdist
-
-# 🏃🏻
-# Go and download your wheel files from wherever you put them. e.g. your CI
-# provider can be configured to store them for you. Put them all into the
-# 'dist' folder.
-
-# Upload using 'twine'
-pipx run twine upload dist/*
-```
-
 ## Automatic method
 
 If you don't need much control over the release of a package, you can set up
-cibuildwheel to deliver the wheels straight to PyPI. You just need to bump the
+your CI provider to deliver the wheels straight to PyPI. You just need to bump the
 version and tag it.
 
-### Generic instructions
+The exact way to set it up varies, depending on which CI provider you're using. But generally, the process goes like this:
 
-Make your SDist with the [build](https://github.com/pypa/build) tool, and your wheels with cibuildwheel. If you can make the files available as
-downloadable artifacts, this make testing before releases easier (depending on your CI provider's options). The "publish" job/step should collect the
-files, and then run `twine upload <paths>` (possibly via [pipx](https://github.com/pypa/pipx)); this should only happen on tags or "releases".
+- Build your wheels with cibuildwheel
+- Build an sdist with the [build](https://github.com/pypa/build) tool
+- Check that the current CI run is happening during a release (e.g. it's in response to a vXX tag)
+- Collect these assets together onto one runner
+- Upload them to PyPI using `twine upload <paths>`
 
 ### GitHub Actions
 
-GitHub actions has pipx in all the runners as a supported package manager, as
-well as several useful actions. Alongside your existing job(s) that runs cibuildwheel to make wheels, you will probably want to build an SDist:
+GitHub actions has pipx in all the runners as a supported package manager, as well as `pypa/gh-action-pypi-publish`, which can be used instead of twine. Alongside your existing job(s) that runs cibuildwheel to make wheels, you will probably want to build an sdist:
 
 ```yaml
   make_sdist:
@@ -64,8 +45,6 @@ well as several useful actions. Alongside your existing job(s) that runs cibuild
 
 Then, you need to publish the artifacts that the previous jobs have built. This final job should run only on release or tag, depending on your preference. It gathers the artifacts from the sdist and wheel jobs and uploads them to PyPI. The release environment (`pypi` in the example below) will be created the first time this workflow runs.
 
-This requires setting this GitHub workflow in your project's PyPI settings (for a [new project](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc)/[existing project](https://docs.pypi.org/trusted-publishers/adding-a-publisher)).
-
 ```yaml
   upload_all:
     needs: [build_wheels, make_sdist]
@@ -84,13 +63,11 @@ This requires setting this GitHub workflow in your project's PyPI settings (for
     - uses: pypa/gh-action-pypi-publish@release/v1
 ```
 
-You should use Dependabot to keep the publish action up to date. In the above
-example, the same name (the default, "artifact" is used for all upload-artifact
-runs, so we can just download all of them in one step into a common directory.
+The above example uses PyPI Trusted Publishing to deliver the wheels, which requires some configuration on the PyPI side for a [new project](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc) or an [existing project](https://docs.pypi.org/trusted-publishers/adding-a-publisher). You can use Dependabot to keep the publish action up to date.
 
 See
 [`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml)
-for an example configuration that automatically upload wheels to PyPI. Also see
+for an example configuration that automatically uploads wheels to PyPI. Also see
 [scikit-hep.org/developer/gha_wheels](https://scikit-hep.org/developer/gha_wheels)
 for a complete guide.
 
@@ -99,3 +76,23 @@ for a complete guide.
 See
 [`examples/travis-ci-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/travis-ci-deploy.yml)
 for an example configuration.
+
+## Manual method
+
+On your development machine, install [pipx](https://pypa.github.io/pipx/) and do the following:
+
+```bash
+# Either download the SDist from your CI, or make it:
+# Clear out your 'dist' folder.
+rm -rf dist
+# Make a source distribution
+pipx run build --sdist
+
+# 🏃🏻
+# Go and download your wheel files from wherever you put them. e.g. your CI
+# provider can be configured to store them for you. Put them all into the
+# 'dist' folder.
+
+# Upload using 'twine'
+pipx run twine upload dist/*
+```
diff --git a/docs/extra.css b/docs/extra.css
index 9cd1c17f4..df7103cea 100644
--- a/docs/extra.css
+++ b/docs/extra.css
@@ -269,62 +269,13 @@ h1, h2, h3, h4, h5, h6 {
 }
 
 
-/* expand all the toctree entries */
-.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,
-.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,
-.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,
-.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,
-.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,
-.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,
-.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,
-.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,
-.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,
-.wy-menu-vertical .toctree-l10.current .toctree-l11>ul {
-  display: block;
-}
-
-/* hide all the buttons */
-.wy-menu-vertical li.current>a button.toctree-expand,
-.wy-menu-vertical li.on a button.toctree-expand,
-.wy-menu-vertical li.toctree-l2 button.toctree-expand,
-.wy-menu-vertical li a button.toctree-expand {
+/* hide the l1 buttons */
+.wy-menu-vertical li.toctree-l1.current>a button.toctree-expand,
+.wy-menu-vertical li.toctree-l1.on>a button.toctree-expand,
+.wy-menu-vertical li.toctree-l1>a button.toctree-expand {
   display: none;
 }
 
-/* toctree layout improvements */
-.wy-menu-vertical a {
-  padding: 0.4em 1.2em;
-}
-.wy-menu-vertical li.current>a,
-.wy-menu-vertical li.on a {
-  font-weight: normal;
-  padding: 0.4em 1.2em;
-  /* border-right: 1px solid #f0f0f0; */
-}
-.wy-menu-vertical li.current a {
-  padding: 0.4em 1.2em;
-}
-.wy-menu-vertical li.toctree-l3 a,
-.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a {
-  padding-left: 1.8em;
-}
-.wy-menu-vertical li.toctree-l2.current>a {
-  padding: 0.4em 1.2em;
-}
-.wy-menu-vertical li.toctree-l2.current>a,
-.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a {
-  background: transparent;
-}
-.wy-menu-vertical li.toctree-l2.current>a:hover,
-.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a:hover {
-  background: #d6d6d6;
-}
-.wy-menu-vertical li.current>a.current {
-  background: #fafafa !important;
-}
-.wy-menu-vertical li.current a {
-
-}
 
 /* word wrap in table cells */
 .wy-table-responsive table td, .wy-table-responsive table th {
diff --git a/docs/extra.js b/docs/extra.js
index 23bed98ce..c6013f535 100644
--- a/docs/extra.js
+++ b/docs/extra.js
@@ -58,3 +58,54 @@ while (true) {
   // this will catch infinite loops which can occur when editing the above
   if (tabConversionIterations++ > 1000) throw 'too many iterations'
 }
+
+/**
+ * Redirects the current page based on the path and fragment identifier (hash) in the URL.
+ *
+ * Example usage:
+ * fragmentRedirect([
+ *  { source: 'setup/#github-actions', destination: 'ci-services' }
+ *  { source: 'faq/#macosx', destination: 'platforms#apple' }
+ * ])
+ */
+function fragmentRedirect(redirects) {
+  const href = window.location.href;
+  const hash = window.location.hash;
+
+  for (const redirect of redirects) {
+    const source = redirect.source;
+    const destination = redirect.destination;
+
+    if (endswith(href, source)) {
+      // Redirect to the destination path, with the same fragment identifier
+      // specified in the destination path, otherwise, keep the same hash
+      // from the current URL.
+      const destinationIncludesHash = destination.includes('#');
+      let newUrl = href.replace(source, destination);
+      if (!destinationIncludesHash) {
+        newUrl += hash;
+      }
+      console.log('Redirecting to:', newUrl);
+      window.location.replace(newUrl);
+      return
+    }
+  }
+}
+
+function endswith(str, suffix) {
+  return str.indexOf(suffix, str.length - suffix.length) !== -1;
+}
+
+fragmentRedirect([
+  { source: 'setup/#github-actions', destination: 'ci-services/' },
+  { source: 'setup/#azure-pipelines', destination: 'ci-services/' },
+  { source: 'setup/#travis-ci', destination: 'ci-services/' },
+  { source: 'setup/#appveyor', destination: 'ci-services/' },
+  { source: 'setup/#circleci', destination: 'ci-services/' },
+  { source: 'setup/#gitlab-ci', destination: 'ci-services/' },
+  { source: 'setup/#cirrus-ci', destination: 'ci-services/' },
+
+  { source: 'faq/#linux-builds-in-containers', destination: 'platforms/#linux-containers' },
+  { source: 'faq/#apple-silicon', destination: 'platforms/#macos-architectures' },
+  { source: 'faq/#windows-arm64', destination: 'platforms/#windows-arm64' },
+]);
diff --git a/docs/faq.md b/docs/faq.md
index 3f74d89d6..40beea950 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -6,88 +6,6 @@ title: Tips and tricks
 
 ## Tips
 
-### Linux builds in containers
-
-Linux wheels are built in [`manylinux`/`musllinux` containers](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 600](https://www.python.org/dev/peps/pep-0600/) / [PEP 656](https://www.python.org/dev/peps/pep-0656/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
-
--   Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using `yum` for `manylinux2014`, `apt-get` for `manylinux_2_31`, `dnf` for `manylinux_2_28` and `apk` for `musllinux_1_1` or `musllinux_1_2`, or manually. The same goes for environment variables that are potentially needed to customize the wheel building.
-
-    `cibuildwheel` supports this by providing the [`CIBW_ENVIRONMENT`](options.md#environment) and [`CIBW_BEFORE_ALL`](options.md#before-all) options to setup the build environment inside the running container.
-
--   The project directory is copied into the container as `/project`, the output directory for the wheels to be copied out is `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system.  Note that `/host` is not available on CircleCI and GitLab CI due to their Docker policies.
-
--   Alternative Docker images can be specified with the `CIBW_MANYLINUX_*_IMAGE`/`CIBW_MUSLLINUX_*_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#linux-image) for more details.
-
-### Building macOS wheels for Apple Silicon {: #apple-silicon}
-
-`cibuildwheel` supports both native builds and cross-compiling between `arm64` (Apple Silicon) and `x86_64` (Intel) architectures, including the cross-compatible `universal2` format.
-
-#### Overview of Mac architectures
-
-You have several choices for wheels for Python 3.8+:
-
-##### `x86_64`
-
-The traditional wheel for Apple, loads on Intel machines, and on
-Apple Silicon when running Python under Rosetta 2 emulation.
-
-Due to a change in naming, Pip 20.3+ (or an installer using packaging 20.5+)
-is required to install a binary wheel on macOS Big Sur.
-
-##### `arm64`
-
-The native wheel for macOS on Apple Silicon.
-
-Requires Pip 20.3+ (or packaging 20.5+) to install.
-
-##### `universal2`
-
-This wheel contains both architectures, causing it to be up to twice the
-size (data files do not get doubled, only compiled code). It requires
-Pip 20.3 (Packaging 20.6+) to load on Intel, and Pip 21.0.1 (Packaging 20.9+)
-to load on Apple Silicon.
-
-The dual-architecture `universal2` has a few benefits, but a key benefit
-to a universal wheel is that a user can bundle these wheels into an
-application and ship a single binary.
-
-However, if you have a large library, then you might prefer to ship
-the two single-arch wheels instead - `x86_64` and `arm64`. In rare cases,
-you might want to build all three, but in that case, pip will not download
-the universal wheels, because it prefers the most specific wheel
-available.
-
-#### What to provide?
-
-Generally speaking, because Pip 20.3 is required for the `universal2` wheel,
-most packages should provide both `x86_64` and one of `universal2`/`arm64`
-wheels. When Pip 20.3+ is common on macOS, then it might be possible to ship
-only the `universal2` wheel.
-
-Opinions vary on which of arch-specific or `universal2` wheels are best - some packagers prefer `universal2` because it's one wheel for all Mac users, so simpler, and easier to build into apps for downstream users. However, because they contain code for both architectures, their file size is larger, meaning they consume more disk space and bandwidth, and are harder to build for some projects.
-
-See [GitHub issue 1333](https://github.com/pypa/cibuildwheel/issues/1333) for more discussion.
-
-#### How?
-
-It's easiest to build `x86_64` wheels on `x86_64` runners, and `arm64` wheels on `arm64` runners.
-
-On GitHub Actions, `macos-14` runners are `arm64`, and `macos-13` runners are `x86_64`. So all you need to do is ensure both are in your build matrix.
-
-#### Cross-compiling
-
-If your CI provider doesn't offer arm64 runners yet, or you want to create `universal2`, you'll have to cross-compile. Cross-compilation can be enabled by adding extra archs to the [`CIBW_ARCHS_MACOS` option](options.md#archs) - e.g. `CIBW_ARCHS_MACOS="x86_64 universal2"`. Cross-compilation is provided by Xcode toolchain v12.2+.
-
-Regarding testing,
-
-- On an arm64 runner, it is possible to test x86_64 wheels and both parts of a universal2 wheel using Rosetta 2 emulation.
-- On an x86_64 runner, arm64 code can be compiled but it can't be tested. `cibuildwheel` will raise a warning to notify you of this - these warnings can be silenced by skipping testing on these platforms: `CIBW_TEST_SKIP: "*_arm64 *_universal2:arm64"`.
-
-!!! note
-    If your project uses **Poetry** as a build backend, cross-compiling on macOS [does not currently work](https://github.com/python-poetry/poetry/issues/7107). In some cases arm64 wheels can be built but their tags will be incorrect, with the platform tag showing `x86_64` instead of `arm64`.
-
-    As a workaround, the tag can be fixed before running delocate to repair the wheel. The [`wheel tags`](https://wheel.readthedocs.io/en/stable/reference/wheel_tags.html) command is ideal for this. See [this workflow](https://gist.github.com/anderssonjohan/49f07e33fc5cb2420515a8ac76dc0c95#file-build-pendulum-wheels-yml-L39-L53) for an example usage of `wheel tags`.
-
 ### Building Linux wheels for non-native archs using emulation  {: #emulation}
 
 cibuildwheel supports building non-native architectures on Linux, via
@@ -247,15 +165,11 @@ Your build might need some compiler flags to be set through environment variable
 Consider incorporating these into your package, for example, in `setup.py` using [`extra_compile_args` or
 `extra_link_args`](https://docs.python.org/3/distutils/setupscript.html#other-options).
 
-### Python 2.7 / PyPy2 wheels
-
-See the [cibuildwheel version 1 docs](https://cibuildwheel.pypa.io/en/1.x/) for information about building Python 2.7 or PyPy2 wheels. There are lots of tricks and workaround there that are no longer required for Python 3 in cibuildwheel 2.
-
 ## Troubleshooting
 
 If your wheel didn't compile, you might have a mistake in your config.
 
-To quickly test your config without doing a git push and waiting for your code to build on CI, you can [test the Linux build in a local Docker container](setup.md#local).
+To quickly test your config without doing a git push and waiting for your code to build on CI, you can [test the Linux build in a local Docker container](platforms.md#linux).
 
 ### Missing dependencies
 
@@ -312,10 +226,6 @@ skip = ["*-musllinux_i686"]
 
 Also see [maturin-action](https://github.com/PyO3/maturin-action) which is optimized for Rust wheels, builds the non-Python Rust modules once, and can cross-compile (and can build 32-bit musl, for example).
 
-### macOS: ModuleNotFoundError
-
-Calling cibuildwheel from a python3 script and getting a `ModuleNotFoundError`? Due to a (fixed) [bug](https://bugs.python.org/issue22490) in CPython, you'll need to [unset the `__PYVENV_LAUNCHER__` variable](https://github.com/pypa/cibuildwheel/issues/133#issuecomment-478288597) before activating a venv.
-
 ### macOS: 'No module named XYZ' errors after running cibuildwheel
 
 `cibuildwheel` on Mac installs the distributions from Python.org system-wide during its operation. This is necessary, but it can cause some confusing errors after cibuildwheel has finished.
@@ -420,11 +330,3 @@ To add the `/d2FH4-` flag to a standard `setup.py` using `setuptools`, the `extr
 ```
 
 To investigate the dependencies of a C extension (i.e., the `.pyd` file, a DLL in disguise) on Windows, [Dependency Walker](http://www.dependencywalker.com/) is a great tool. For diagnosing a failing import, the [dlltracer](https://pypi.org/project/dlltracer/) tool may also provide additional details.
-
-### Windows ARM64 builds {: #windows-arm64}
-
-`cibuildwheel` supports cross-compiling `ARM64` wheels on all Windows runners, but a native ARM64 runner is required for testing. On non-native runners, tests for ARM64 wheels will be automatically skipped with a warning. Add `"*-win_arm64"` to your `CIBW_TEST_SKIP` setting to suppress the warning.
-
-Cross-compilation on Windows relies on a supported build backend. Supported backends use an environment variable to specify their target platform (the one they are compiling native modules for, as opposed to the one they are running on), which is set in [cibuildwheels/windows.py](https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/windows.py) before building. Currently, `setuptools>=65.4.1` and `setuptools_rust` are the only supported backends.
-
-By default, `ARM64` is not enabled when running on non-ARM64 runners. Use [`CIBW_ARCHS`](options.md#archs) to select it.
diff --git a/docs/options.md b/docs/options.md
index cee3b6d7c..de4317b4d 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1,244 +1,5 @@
 # Options
 
-## Setting options
-
-cibuildwheel can either be configured using environment variables, or from
-config file such as `pyproject.toml`.
-
-### Environment variables {: #environment-variables}
-
-Environment variables can be set in your CI config. For example, to configure
-cibuildwheel to run tests, add the following YAML to your CI config file:
-
-!!! tab "GitHub Actions"
-
-    > .github/workflows/*.yml ([docs](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables)) (can be global, in job, or in step)
-
-    ```yaml
-    env:
-      CIBW_TEST_REQUIRES: pytest
-      CIBW_TEST_COMMAND: "pytest {project}/tests"
-    ```
-
-!!! tab "Azure Pipelines"
-
-    > azure-pipelines.yml ([docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables))
-
-    ```yaml
-    variables:
-      CIBW_TEST_REQUIRES: pytest
-      CIBW_TEST_COMMAND: "pytest {project}/tests"
-    ```
-
-!!! tab "Travis CI"
-
-    > .travis.yml ([docs](https://docs.travis-ci.com/user/environment-variables/))
-
-    ```yaml
-    env:
-      global:
-        - CIBW_TEST_REQUIRES=pytest
-        - CIBW_TEST_COMMAND="pytest {project}/tests"
-    ```
-
-!!! tab "AppVeyor"
-
-    > appveyor.yml ([docs](https://www.appveyor.com/docs/build-configuration/#environment-variables))
-
-    ```yaml
-    environment:
-      global:
-        CIBW_TEST_REQUIRES: pytest
-        CIBW_TEST_COMMAND: "pytest {project}\\tests"
-    ```
-
-!!! tab "CircleCI"
-
-    > .circleci/config.yml ([docs](https://circleci.com/docs/2.0/configuration-reference/#environment))
-
-    ```yaml
-    jobs:
-      job_name:
-        environment:
-          CIBW_TEST_REQUIRES: pytest
-          CIBW_TEST_COMMAND: "pytest {project}/tests"
-    ```
-
-!!! tab "Gitlab CI"
-
-    > .gitlab-ci.yml ([docs](https://docs.gitlab.com/ee/ci/variables/README.html#create-a-custom-variable-in-gitlab-ciyml))
-
-    ```yaml
-    linux:
-      variables:
-        CIBW_TEST_REQUIRES: pytest
-        CIBW_TEST_COMMAND: "pytest {project}/tests"
-    ```
-
-!!! tab "Cirrus CI"
-
-    > .cirrus.yml ([docs](https://cirrus-ci.org/guide/writing-tasks/#environment-variables))
-
-    ```yaml
-    env:
-      CIBW_TEST_REQUIRES: pytest
-      CIBW_TEST_COMMAND: "pytest {project}/tests"
-    ```
-
-### Configuration file {: #configuration-file}
-
-You can configure cibuildwheel with a config file, such as `pyproject.toml`.
-Options have the same names as the environment variable overrides, but are
-placed in `[tool.cibuildwheel]` and are lower case, with dashes, following
-common [TOML][] practice. Anything placed in subsections `linux`, `windows`,
-`macos`, or `pyodide` will only affect those platforms. Lists can be used
-instead of strings for items that are naturally a list. Multiline strings also
-work just like in the environment variables. Environment variables will take
-precedence if defined.
-
-The example above using environment variables could have been written like this:
-
-```toml
-[tool.cibuildwheel]
-test-requires = "pytest"
-test-command = "pytest {project}/tests"
-```
-
-The complete set of defaults for the current version of cibuildwheel are shown below:
-
-```toml
-{% include "../cibuildwheel/resources/defaults.toml" %}
-```
-
-
-!!! tip
-    Static configuration works across all CI systems, and can be used locally if
-    you run `cibuildwheel --platform linux`. This is preferred, but environment
-    variables are better if you need to change per-matrix element
-    (`CIBW_BUILD` is often in this category, for example), or if you cannot or do
-    not want to change a `pyproject.toml` file. You can specify a different file to
-    use with `--config-file` on the command line, as well.
-
-### Configuration overrides {: #overrides }
-
-One feature specific to the configuration files is the ability to override
-settings based on selectors. To use, add a ``tool.cibuildwheel.overrides``
-array, and specify a ``select`` string. Then any options you set will only
-apply to items that match that selector. These are applied in order, with later
-matches overriding earlier ones if multiple selectors match. Environment
-variables always override static configuration.
-
-A few of the options below have special handling in overrides. A different
-`before-all` will trigger a new container to launch on Linux, and cannot be
-overridden on macOS or Windows.  Overriding the image on linux will also
-trigger new containers, one per image. Some commands are not supported;
-`output-dir`, build/skip/test_skip selectors, and architectures cannot be
-overridden.
-
-You can specify a table of overrides in `inherit={}`, any list or table in this
-list will inherit from previous overrides or the main configuration. The valid
-options are `"none"` (the default), `"append"`, and `"prepend"`.
-
-##### Examples:
-
-```toml
-[tool.cibuildwheel.linux]
-before-all = "yum install mylib"
-test-command = "echo 'installed'"
-
-[[tool.cibuildwheel.overrides]]
-select = "*-musllinux*"
-before-all = "apk add mylib"
-```
-
-This example will override the before-all command on musllinux only, but will
-still run the test-command. Note the double brackets, this is an array in TOML,
-which means it can be given multiple times.
-
-```toml
-[tool.cibuildwheel]
-# Normal options, etc.
-manylinux-x86_64-image = "manylinux_2_34"
-
-[[tool.cibuildwheel.overrides]]
-select = "cp38-*"
-manylinux-x86_64-image = "manylinux2014"
-
-[[tool.cibuildwheel.overrides]]
-select = "cp3{9,10}-*"
-manylinux-x86_64-image = "manylinux_2_28"
-```
-
-This example will build CPython 3.8 wheels on manylinux2014, CPython 3.9-3.10
-wheels on manylinux_2_28, and manylinux_2_34 wheels for any newer Python
-(like 3.10).
-
-```toml
-[tool.cibuildwheel]
-environment = {FOO="BAR", "HAM"="EGGS"}
-test-command = ["pyproject"]
-
-[[tool.cibuildwheel.overrides]]
-select = "cp311*"
-
-inherit.test-command = "prepend"
-test-command = ["pyproject-before"]
-
-inherit.environment="append"
-environment = {FOO="BAZ", "PYTHON"="MONTY"}
-
-[[tool.cibuildwheel.overrides]]
-select = "cp311*"
-inherit.test-command = "append"
-test-command = ["pyproject-after"]
-```
-
-This example will provide the command `"pyproject-before && pyproject && pyproject-after"`
-on Python 3.11, and will have `environment = {FOO="BAZ", "PYTHON"="MONTY", "HAM"="EGGS"}`.
-
-
-### Extending existing options {: #inherit }
-
-In the TOML configuration, you can choose how tables and lists are inherited.
-By default, all values are overridden completely (`"none"`) but sometimes you'd
-rather `"append"` or `"prepend"` to an existing list or table. You can do this
-with the `inherit` table in overrides.  For example, if you want to add an environment
-variable for CPython 3.11, without `inherit` you'd have to repeat all the
-original environment variables in the override. With `inherit`, it's just:
-
-```toml
-[[tool.cibuildwheel.overrides]]
-select = "cp311*"
-inherit.environment = "append"
-environment.NEWVAR = "Added!"
-```
-
-For a table, `"append"` will replace a key if it exists, while `"prepend"` will
-only add a new key, older keys take precedence.
-
-Lists are also supported (and keep in mind that commands are lists). For
-example, you can print a message before and after a wheel is repaired:
-
-```toml
-[[tool.cibuildwheel.overrides]]
-select = "*"
-inherit.repair-wheel-command = "prepend"
-repair-wheel-command = "echo 'Before repair'"
-
-[[tool.cibuildwheel.overrides]]
-select = "*"
-inherit.repair-wheel-command = "append"
-repair-wheel-command = "echo 'After repair'"
-```
-
-As seen in this example, you can have multiple overrides match - they match top
-to bottom, with the config being accumulated. If you need platform-specific
-inheritance, you can use `select = "*-????linux_*"` for Linux, `select =
-"*-win_*"` for Windows, and `select = "*-macosx_*"` for macOS. As always,
-environment variables will completely override any TOML configuration.
-
-## Options summary
-
 <div class="options-toc"></div>
 
 ## Build selection
@@ -265,13 +26,13 @@ This option can also be set using the [command-line option](#command-line) `--pl
 
     ```bash
     export CIBW_BUILD='cp37-*'
-    export CIBW_TEST_COMMAND='pytest {package}/tests'
+    export CIBW_TEST_COMMAND='pytest ./tests'
     cibuildwheel --platform linux .
     ```
 
     Linux builds are the easiest to test locally, because all the build tools are supplied in the container, and they run exactly the same locally as in CI.
 
-    This is even more convenient if you store your cibuildwheel config in [`pyproject.toml`](#configuration-file).
+    This is even more convenient if you store your cibuildwheel config in [`pyproject.toml`](configuration.md#configuration-file).
 
     You can also run a single identifier with `--only <identifier>`. This will
     not require `--platform` or `--arch`, and will override any build/skip
@@ -310,7 +71,7 @@ Windows arm64 platform support is experimental.
 For an experimental WebAssembly build with `--platform pyodide`,
 `cp312-pyodide_wasm32` is the only platform identifier.
 
-See the [cibuildwheel 1 documentation](https://cibuildwheel.pypa.io/en/1.x/) for past end-of-life versions of Python, and PyPy2.7.
+See the [cibuildwheel 2 documentation](https://cibuildwheel.pypa.io/en/2.x/) for past end-of-life versions of Python.
 
 #### Examples
 
@@ -409,14 +170,11 @@ See the [cibuildwheel 1 documentation](https://cibuildwheel.pypa.io/en/1.x/) for
 
 A list of architectures to build.
 
-On macOS, this option can be used to [cross-compile](faq.md#cross-compiling)
-between `x86_64`, `universal2` and `arm64`.
+On macOS, this option can be used to [cross-compile](platforms.md#macos-architectures) between `x86_64`, `universal2` and `arm64`.
 
-On Linux, this option can be used to build non-native architectures under
-emulation. See [this guide](faq.md#emulation) for more information.
+On Linux, this option can be used to build [non-native architectures under emulation](faq.md#emulation).
 
-On Windows, this option can be used to compile for `ARM64` from an Intel
-machine, provided the cross-compiling tools are installed.
+On Windows, this option can be used to [compile for `ARM64` from an Intel machine](platforms.md#windows-arm64), provided the cross-compiling tools are installed.
 
 Options:
 
@@ -649,19 +407,19 @@ This option can also be set using the [command-line option](#command-line)
 ## Build customization
 
 ### `CIBW_BUILD_FRONTEND` {: #build-frontend}
-> Set the tool to use to build, either "build" (default), "build/[uv/]", or "pip"
+> Set the tool to use to build, either "build" (default), "build\[uv\]", or "pip"
 
 Options:
 
-- `pip[;args: ...]`
 - `build[;args: ...]`
+- `build[uv][;args: ...]`
+- `pip[;args: ...]`
 
-Default: `pip`
+Default: `build`
 
-Choose which build frontend to use. Can either be "build", which will run
-`python -m build --wheel`, or "pip", which will run `python -m pip wheel`.
+Choose which build frontend to use.
 
-You can also use "build\[uv\]", which will use an external [uv][] everywhere
+You can use "build\[uv\]", which will use an external [uv][] everywhere
 possible, both through `--installer=uv` passed to build, as well as when making
 all build and test environments. This will generally speed up cibuildwheel.
 Make sure you have an external uv on Windows and macOS, either by
@@ -672,15 +430,9 @@ setuptools on Python < 3.12 and pip are not installed if using uv.
 
 Pyodide ignores this setting, as only "build" is supported.
 
-You can specify extra arguments to pass to `pip wheel` or `build` using the
+You can specify extra arguments to pass to the build frontend using the
 optional `args` option.
 
-!!! tip
-    Until v2.0.0, [pip][] was the only way to build wheels, and is still the
-    default. However, we expect that at some point in the future, cibuildwheel
-    will change the default to [build][], in line with the PyPA's recommendation.
-    If you want to try `build` before this, you can use this option.
-
 !!! warning
     If you are using `build[uv]` and are passing `--no-isolation` or `-n`, we
     will detect this and avoid passing `--installer=uv` to build, but still
@@ -1471,13 +1223,18 @@ automatically and available for import from the tests. If this variable is not
 set, your wheel will not be installed after building.
 
 By default, tests are executed from your project directory. When specifying
-`CIBW_TEST_COMMAND`, you can use the placeholders `{project}` and `{package}` to
-pass in the location of your test code:
+`CIBW_TEST_COMMAND`, you can optionally use the placeholders `{package}` and
+`{project}` to pass in the location of your test code:
 
-- `{project}` is an absolute path to the project root - the working directory
-  where cibuildwheel was called.
 - `{package}` is the path to the package being built - the `package_dir`
   argument supplied to cibuildwheel on the command line.
+- `{project}` is an absolute path to the project root - the working directory
+  where cibuildwheel was called.
+
+Using `{package}` or `{project}` used to be required, but since cibuildwheel
+3.0, tests are run from the project root by default. This means that you can
+use relative paths in your test command, and they will be relative to the
+project root.
 
 Alternatively, you can use the [`CIBW_TEST_SOURCES`](#test-sources) setting to
 create a temporary folder populated with a specific subset of project files to
@@ -1496,15 +1253,15 @@ Platform-specific environment variables are also available:<br/>
 
     ```yaml
     # Run the package tests using `pytest`
-    CIBW_TEST_COMMAND: pytest {package}/tests
+    CIBW_TEST_COMMAND: pytest ./tests
 
     # Trigger an install of the package, but run nothing of note
     CIBW_TEST_COMMAND: "echo Wheel installed"
 
     # Multi-line example - join with && on all platforms
     CIBW_TEST_COMMAND: >
-      pytest {package}/tests &&
-      python {package}/test.py
+      pytest ./tests &&
+      python ./test.py
     ```
 
 !!! tab examples "pyproject.toml"
@@ -1512,28 +1269,20 @@ Platform-specific environment variables are also available:<br/>
     ```toml
     [tool.cibuildwheel]
     # Run the package tests using `pytest`
-    test-command = "pytest {package}/tests"
+    test-command = "pytest ./tests"
 
     # Trigger an install of the package, but run nothing of note
     test-command = "echo Wheel installed"
 
     # Multiline example
     test-command = [
-      "pytest {package}/tests",
-      "python {package}/test.py",
+      "pytest ./tests",
+      "python ./test.py",
     ]
     ```
 
     In configuration files, you can use an array, and the items will be joined with `&&`.
 
-!!! note
-
-    It isn't recommended to `cd` to your project directory before running tests,
-    because Python might resolve `import yourpackage` relative to the working dir,
-    and we want to test the wheel you just built. However, if you're sure that's not
-    an issue for you and your workflow requires it, on Windows you should do `cd /d`,
-    because the CWD and project dir might be on different drives.
-
 ### `CIBW_BEFORE_TEST` {: #before-test}
 > Execute a shell command before testing each wheel
 
@@ -1873,8 +1622,8 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
   .options-toc {
     display: grid;
     grid-template-columns: fit-content(20%) 1fr;
-    grid-gap: 16px 32px;
-    gap: 16px 32px;
+    grid-gap: 10px 20px;
+    gap: 10px 20px;
     font-size: 90%;
     margin-bottom: 28px;
     margin-top: 28px;
@@ -1894,8 +1643,11 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
     margin-top: 0;
   }
   .options-toc a.option {
-    display: block;
-    margin-bottom: 5px;
+    display: inline-block;
+    margin-bottom: 3px;
+  }
+  .options-toc a.option code {
+    font-size: 80%;
   }
   h3 code {
     font-size: 100%;
@@ -1994,5 +1746,3 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
     console.log('readme options markdown\n', markdown)
   });
 </script>
-
-[TOML]: https://toml.io
diff --git a/docs/platforms.md b/docs/platforms.md
new file mode 100644
index 000000000..115e91455
--- /dev/null
+++ b/docs/platforms.md
@@ -0,0 +1,236 @@
+---
+title: Platforms
+---
+# Platforms
+
+## Linux
+
+### System requirements
+
+If you've got [Docker](https://www.docker.com/get-started/) installed on your development machine, you can run a Linux build.
+
+!!! tip
+    You can run the Linux build on any platform. Even Windows can run
+    Linux containers these days, but there are a few hoops to jump
+    through. Check [this document](https://docs.microsoft.com/en-us/virtualization/windowscontainers/quick-start/quick-start-windows-10-linux)
+    for more info.
+
+Because the builds are happening in manylinux Docker containers, they're perfectly reproducible.
+
+The only side effect to your system will be docker images being pulled.
+
+### Build containers {: #linux-containers}
+
+Linux wheels are built in [`manylinux`/`musllinux` containers](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 600](https://www.python.org/dev/peps/pep-0600/) / [PEP 656](https://www.python.org/dev/peps/pep-0656/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
+
+-   Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using `yum` for `manylinux2014`, `apt-get` for `manylinux_2_31`, `dnf` for `manylinux_2_28` and `apk` for `musllinux_1_1` or `musllinux_1_2`, or manually. The same goes for environment variables that are potentially needed to customize the wheel building.
+
+    `cibuildwheel` supports this by providing the [`CIBW_ENVIRONMENT`](options.md#environment) and [`CIBW_BEFORE_ALL`](options.md#before-all) options to setup the build environment inside the running container.
+
+-   The project directory is copied into the container as `/project`, the output directory for the wheels to be copied out is `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system.  Note that `/host` is not available on CircleCI and GitLab CI due to their Docker policies.
+
+-   Alternative Docker images can be specified with the `CIBW_MANYLINUX_*_IMAGE`/`CIBW_MUSLLINUX_*_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#linux-image) for more details.
+
+## macOS
+
+### System requirements
+
+You need to have native build tools installed. Use `xcode-select --install` to install the Xcode command line tools.
+
+Because the builds are happening without full isolation, there might be some differences compared to CI builds (Xcode version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
+
+In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. `Cache folder: /Users/Matt/Library/Caches/cibuildwheel`). You can override the cache folder using the `CIBW_CACHE_PATH` environment variable.
+
+!!! warning
+    cibuildwheel uses official python.org macOS installers for CPython but those can only be installed globally.
+
+    In order not to mess with your system, cibuildwheel won't install those if they are missing. Instead, it will error out with a message to let you install the missing CPython:
+
+    ```console
+    Error: CPython 3.9 is not installed.
+    cibuildwheel will not perform system-wide installs when running outside of CI.
+    To build locally, install CPython 3.9 on this machine, or, disable this version of Python using CIBW_SKIP=cp39-macosx_*
+
+    Download link: https://www.python.org/ftp/python/3.9.8/python-3.9.8-macosx10.9.pkg
+    ```
+
+### macOS Version Compatibility
+
+macOS allows you to specify a "deployment target" version that will ensure backwards compatibility with older versions of macOS. For most projects, the way to do this is to set the `MACOSX_DEPLOYMENT_TARGET` environment variable.
+
+macOS builds will honor the `MACOSX_DEPLOYMENT_TARGET` environment variable to control the minimum supported macOS version for generated wheels. The lowest value you can set `MACOSX_DEPLOYMENT_TARGET` is as follows:
+
+| Arch  | Python version range | Minimum target |
+|-------|----------------------|----------------|
+| Intel | CPython 3.8-3.11     | 10.9           |
+| Intel | CPython 3.12+        | 10.13          |
+| AS    | CPython or PyPy      | 11             |
+| Intel | PyPy 3.8             | 10.13          |
+| Intel | PyPy 3.9+            | 10.15          |
+
+If you set the value lower, cibuildwheel will cap it to the lowest supported value for each target as needed.
+
+!!! note
+    For Rust-based extensions, `Rustc` requires `MACOSX_DEPLOYMENT_TARGET` to be at
+    least 10.12. However, `cibuildwheel` defaults to 10.9 for
+    **Intel / CPython 3.8-3.11** builds. Users must manually set
+    `MACOSX_DEPLOYMENT_TARGET` to 10.12 or higher when building Rust extensions.
+
+### macOS architectures
+
+`cibuildwheel` supports both native builds and cross-compiling between `arm64` (Apple Silicon) and `x86_64` (Intel) architectures, including the cross-compatible `universal2` format. By default, macOS builds will build a single architecture wheel, using the build machine's architecture.
+
+If you need to support both x86_64 and Apple Silicon, you can use the `CIBW_ARCHS` environment variable to specify the architectures you want to build, or the value `universal2` to build a multi-architecture wheel. cibuildwheel _will_ test x86_64 wheels (or the x86_64 slice of a `universal2` wheel) when running on Apple Silicon hardware using Rosetta 2 emulation, but it is *not* possible to test Apple Silicon wheels on x86_64 hardware.
+
+#### Overview of Mac architectures
+
+##### `x86_64`
+
+The traditional wheel for Apple, loads on Intel machines, and on
+Apple Silicon when running Python under Rosetta 2 emulation.
+
+Due to a change in naming, Pip 20.3+ (or an installer using packaging 20.5+)
+is required to install a binary wheel on macOS Big Sur.
+
+##### `arm64`
+
+The native wheel for macOS on Apple Silicon.
+
+##### `universal2`
+
+This wheel contains both architectures, causing it to be up to twice the
+size (data files do not get doubled, only compiled code).
+
+The dual-architecture `universal2` has a few benefits, but a key benefit
+to a universal wheel is that a user can bundle these wheels into an
+application and ship a single binary.
+
+However, if you have a large library, then you might prefer to ship
+the two single-arch wheels instead - `x86_64` and `arm64`. In rare cases,
+you might want to build all three, but in that case, pip will not download
+the universal wheels, because it prefers the most specific wheel
+available.
+
+#### What to provide?
+
+Opinions vary on which of arch-specific or `universal2` wheels are best - some packagers prefer `universal2` because it's one wheel for all Mac users, so simpler, and easier to build into apps for downstream users. However, because they contain code for both architectures, their file size is larger, meaning they consume more disk space and bandwidth, and are harder to build for some projects.
+
+See [GitHub issue 1333](https://github.com/pypa/cibuildwheel/issues/1333) for more discussion.
+
+#### How?
+
+It's easiest to build `x86_64` wheels on `x86_64` runners, and `arm64` wheels on `arm64` runners.
+
+On GitHub Actions, `macos-14` runners are `arm64`, and `macos-13` runners are `x86_64`. So all you need to do is ensure both are in your build matrix.
+
+#### Cross-compiling
+
+If your CI provider doesn't offer arm64 runners yet, or you want to create `universal2`, you'll have to cross-compile. Cross-compilation can be enabled by adding extra archs to the [`CIBW_ARCHS_MACOS` option](options.md#archs) - e.g. `CIBW_ARCHS_MACOS="x86_64 universal2"`. Cross-compilation is provided by Xcode toolchain v12.2+.
+
+Regarding testing,
+
+- On an arm64 runner, it is possible to test x86_64 wheels and both parts of a universal2 wheel using Rosetta 2 emulation.
+- On an x86_64 runner, arm64 code can be compiled but it can't be tested. `cibuildwheel` will raise a warning to notify you of this - these warnings can be silenced by skipping testing on these platforms: `CIBW_TEST_SKIP: "*_arm64 *_universal2:arm64"`.
+
+!!! note
+    If your project uses **Poetry** as a build backend, cross-compiling on macOS [does not currently work](https://github.com/python-poetry/poetry/issues/7107). In some cases arm64 wheels can be built but their tags will be incorrect, with the platform tag showing `x86_64` instead of `arm64`.
+
+    As a workaround, the tag can be fixed before running delocate to repair the wheel. The [`wheel tags`](https://wheel.readthedocs.io/en/stable/reference/wheel_tags.html) command is ideal for this. See [this workflow](https://gist.github.com/anderssonjohan/49f07e33fc5cb2420515a8ac76dc0c95#file-build-pendulum-wheels-yml-L39-L53) for an example usage of `wheel tags`.
+
+
+## Windows
+
+### System requirements
+
+You must have native build tools (i.e., Visual Studio) installed.
+
+Because the builds are happening without full isolation, there might be some differences compared to CI builds (Visual Studio version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
+
+In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. `Cache folder: C:\Users\Matt\AppData\Local\pypa\cibuildwheel\Cache`). You can override the cache folder using the ``CIBW_CACHE_PATH`` environment variable.
+
+### Windows ARM64 builds {: #windows-arm64}
+
+`cibuildwheel` supports cross-compiling `ARM64` wheels on all Windows runners, but a native ARM64 runner is required for testing. On non-native runners, tests for ARM64 wheels will be automatically skipped with a warning. Add `"*-win_arm64"` to your `CIBW_TEST_SKIP` setting to suppress the warning.
+
+Cross-compilation on Windows relies on a supported build backend. Supported backends use an environment variable to specify their target platform (the one they are compiling native modules for, as opposed to the one they are running on), which is set in [cibuildwheels/windows.py](https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/windows.py) before building. Currently, `setuptools>=65.4.1` and `setuptools_rust` are the only supported backends.
+
+By default, `ARM64` is not enabled when running on non-ARM64 runners. Use [`CIBW_ARCHS`](options.md#archs) to select it.
+
+## Pyodide/WebAssembly {: #pyodide}
+
+Pyodide is offered as an experimental feature in cibuildwheel.
+
+### Prerequisites
+
+You need to have a matching host version of Python (unlike all other cibuildwheel platforms). Linux host highly recommended; macOS hosts may work (e.g. invoking `pytest` directly in [`CIBW_TEST_COMMAND`](options.md#test-command) is [currently failing](https://github.com/pyodide/pyodide/issues/4802)) and Windows hosts will not work.
+
+### Specifying a pyodide build
+
+You must target pyodide with `--platform pyodide` (or use `--only` on the identifier).
+
+## iOS
+
+### System requirements
+
+You must be building on a macOS machine, with Xcode installed. The Xcode installation must have an iOS SDK available, with all license agreements agreed to by the user. To check if an iOS SDK is available, open the Xcode settings panel, and check the Platforms tab. This will also ensure that license agreements have been acknowledged.
+
+Building iOS wheels also requires a working macOS Python installation. See the notes on [macOS builds](#macos) for details about configuration of the macOS environment.
+
+### Specifying an iOS build
+
+iOS is effectively 2 platforms - physical devices, and simulators. While the API for these two platforms are identical, the ABI is not compatible, even when dealing with a device and simulator with the same CPU architecture. For this reason, the architecture specification for iOS builds includes *both* the CPU architecture *and* the ABI that is being targeted. There are three possible values for architecture on iOS; the values match those used by `sys.implementation._multiarch` when running on iOS (with hyphens replaced with underscores, matching wheel filename normalization):
+
+* `arm64_iphoneos` (for physical iOS devices);
+* `arm64_iphonesimulator` (for iOS simulators running on Apple Silicon macOS machines); and
+* `x64_64_iphonesimulator` (for iOS simulators running on Intel macOS machines).
+
+By default, cibuildwheel will build wheels for all three of these targets.
+
+If you need to specify different compilation flags or other properties on a per-ABI or per-CPU basis, you can use [configuration overrides](configuration.md#overrides) with a `select` clause that targets the specific ABI or architecture. For example, consider the following example:
+
+```
+[tool.cibuildwheel.ios]
+test-sources = ["tests"]
+test-requires = ["pytest"]
+
+[[tool.cibuildwheel.overrides]]
+select = "*_iphoneos"
+environment.PATH = "/path/to/special/device/details:..."
+
+[[tool.cibuildwheel.overrides]]
+select = "*-ios_arm64_*"
+inherit.test-requires = "append"
+test-requires = ["arm64-testing-helper"]
+```
+
+This configuration would:
+
+ * Specify a `test-sources` and `test-requires` for all iOS targets;
+ * Add a `PATH` setting that will be used on physical iOS devices; and
+ * Add `arm64-testing-helper` to the test environment for all ARM64 iOS devices (whether simulator or device).
+
+### iOS version compatibility
+
+iOS builds will honor the `IPHONEOS_DEPLOYMENT_TARGET` environment variable to set the minimum supported API version for generated wheels. This will default to `13.0` if the environment variable isn't set.
+
+### Cross platform builds
+
+iOS builds are *cross platform builds*, as it not possible to run compilers and other build tools "on device". The pre-compiled iOS binaries used to support iOS builds include tooling that can convert any virtual environment into a cross platform virtual environment - that is, an environment that can run binaries on the build machine (macOS), but, if asked, will respond as if it is an iOS machine. This allows `pip`, `build`, and other build tools to perform iOS-appropriate behaviour.
+
+### Build frontend support
+
+iOS builds support both the `pip` and `build` build frontends. In principle, support for `uv` with the `build[uv]` frontend should be possible, but `uv` [doesn't currently have support for cross-platform builds](https://github.com/astral-sh/uv/issues/7957), and [doesn't have support for iOS (or Android) tags](https://github.com/astral-sh/uv/issues/8029).
+
+### Build environment
+
+The environment used to run builds does not inherit the full user environment - in particular, `PATH` is deliberately re-written. This is because UNIX C tooling doesn't do a great job differentiating between "macOS ARM64" and "iOS ARM64" binaries. If (for example) Homebrew is on the path when compilation commands are invoked, it's easy for a macOS version of a library to be linked into the iOS binary, rendering it unusable on iOS. To prevent this, iOS builds always force `PATH` to a "known minimal" path, that includes only the bare system utilities, and the iOS compiler toolchain.
+
+If your project requires additional tools to build (such as `cmake`, `ninja`, or `rustc`), those tools must be explicitly declared as cross-build tools using [`CIBW_XBUILD_TOOLS`](options.md#xbuild-tools). *Any* tool used by the build process must be included in the `CIBW_XBUILD_TOOLS` list, not just tools that cibuildwheel will invoke directly. For example, if your build script invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your cross-build tools list.
+
+### Tests
+
+If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices.
+
+The iOS test environment can't support running shell scripts, so the [`CIBW_TEST_COMMAND`](options.md#test-command) value must be specified as if it were a command line being passed to `python -m ...`. In addition, the project must use [`CIBW_TEST_SOURCES`](options.md#test-sources) to specify the minimum subset of files that should be copied to the test environment. This is because the test must be run "on device", and the simulator device will not have access to the local project directory.
+
+The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <CIBW_TEST_COMMAND>`.
diff --git a/docs/platforms/ios.md b/docs/platforms/ios.md
deleted file mode 100644
index 0c93ba052..000000000
--- a/docs/platforms/ios.md
+++ /dev/null
@@ -1,70 +0,0 @@
----
-title: 'iOS'
----
-
-# iOS builds
-
-## Pre-requisites
-
-You must be building on a macOS machine, with Xcode installed. The Xcode installation must have an iOS SDK available, with all license agreements agreed to by the user. To check if an iOS SDK is available, open the Xcode settings panel, and check the Platforms tab. This will also ensure that license agreements have been acknowledged.
-
-Building iOS wheels also requires a working macOS Python installation. See the notes on [macOS builds](./macos.md) for details about configuration of the macOS environment.
-
-## Specifying an iOS build
-
-iOS is effectively 2 platforms - physical devices, and simulators. While the API for these two platforms are identical, the ABI is not compatible, even when dealing with a device and simulator with the same CPU architecture. For this reason, the architecture specification for iOS builds includes *both* the CPU architecture *and* the ABI that is being targeted. There are three possible values for architecture on iOS; the values match those used by `sys.implementation._multiarch` when running on iOS (with hyphens replaced with underscores, matching wheel filename normalization):
-
-* `arm64_iphoneos` (for physical iOS devices);
-* `arm64_iphonesimulator` (for iOS simulators running on Apple Silicon macOS machines); and
-* `x64_64_iphonesimulator` (for iOS simulators running on Intel macOS machines).
-
-By default, cibuildwheel will build wheels for all three of these targets.
-
-If you need to specify different compilation flags or other properties on a per-ABI or per-CPU basis, you can use [configuration overrides](../../options/#overrides) with a `select` clause that targets the specific ABI or architecture. For example, consider the following example:
-
-```
-[tool.cibuildwheel.ios]
-test-sources = ["tests"]
-test-requires = ["pytest"]
-
-[[tool.cibuildwheel.overrides]]
-select = "*_iphoneos"
-environment.PATH = "/path/to/special/device/details:..."
-
-[[tool.cibuildwheel.overrides]]
-select = "*-ios_arm64_*"
-inherit.test-requires = "append"
-test-requires = ["arm64-testing-helper"]
-```
-
-This configuration would:
-
- * Specify a `test-sources` and `test-requires` for all iOS targets;
- * Add a `PATH` setting that will be used on physical iOS devices; and
- * Add `arm64-testing-helper` to the test environment for all ARM64 iOS devices (whether simulator or device).
-
-## iOS version compatibility
-
-iOS builds will honor the `IPHONEOS_DEPLOYMENT_TARGET` environment variable to set the minimum supported API version for generated wheels. This will default to `13.0` if the environment variable isn't set.
-
-## Cross platform builds
-
-iOS builds are *cross platform builds*, as it not possible to run compilers and other build tools "on device". The pre-compiled iOS binaries used to support iOS builds include tooling that can convert any virtual environment into a cross platform virtual environment - that is, an environment that can run binaries on the build machine (macOS), but, if asked, will respond as if it is an iOS machine. This allows `pip`, `build`, and other build tools to perform iOS-appropriate behaviour.
-
-## Build frontend support
-
-iOS builds support both the `pip` and `build` build frontends. In principle, support for `uv` with the `build[uv]` frontend should be possible, but `uv` [doesn't currently have support for cross-platform builds](https://github.com/astral-sh/uv/issues/7957), and [doesn't have support for iOS (or Android) tags](https://github.com/astral-sh/uv/issues/8029).
-
-## Build environment
-
-The environment used to run builds does not inherit the full user environment - in particular, `PATH` is deliberately re-written. This is because UNIX C tooling doesn't do a great job differentiating between "macOS ARM64" and "iOS ARM64" binaries. If (for example) Homebrew is on the path when compilation commands are invoked, it's easy for a macOS version of a library to be linked into the iOS binary, rendering it unusable on iOS. To prevent this, iOS builds always force `PATH` to a "known minimal" path, that includes only the bare system utilities, and the iOS compiler toolchain.
-
-If your project requires additional tools to build (such as `cmake`, `ninja`, or `rustc`), those tools must be explicitly declared as cross-build tools using [`CIBW_XBUILD_TOOLS`](../../options#xbuild-tools). *Any* tool used by the build process must be included in the `CIBW_XBUILD_TOOLS` list, not just tools that cibuildwheel will invoke directly. For example, if your build script invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your cross-build tools list.
-
-## Tests
-
-If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices.
-
-The iOS test environment can't support running shell scripts, so the [`CIBW_TEST_COMMAND`](../../options#test-command) value must be specified as if it were a command line being passed to `python -m ...`. In addition, the project must use [`CIBW_TEST_SOURCES`](../../options#test-sources) to specify the minimum subset of files that should be copied to the test environment. This is because the test must be run "on device", and the simulator device will not have access to the local project directory.
-
-The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <CIBW_TEST_COMMAND>`.
diff --git a/docs/platforms/linux.md b/docs/platforms/linux.md
deleted file mode 100644
index ef86439c3..000000000
--- a/docs/platforms/linux.md
+++ /dev/null
@@ -1,13 +0,0 @@
-### Linux builds
-
-If you've got [Docker](https://www.docker.com/products/docker-desktop) installed on your development machine, you can run a Linux build.
-
-!!! tip
-    You can run the Linux build on any platform. Even Windows can run
-    Linux containers these days, but there are a few hoops to jump
-    through. Check [this document](https://docs.microsoft.com/en-us/virtualization/windowscontainers/quick-start/quick-start-windows-10-linux)
-    for more info.
-
-Because the builds are happening in manylinux Docker containers, they're perfectly reproducible.
-
-The only side effect to your system will be docker images being pulled.
diff --git a/docs/platforms/macos.md b/docs/platforms/macos.md
deleted file mode 100644
index bf07e09c0..000000000
--- a/docs/platforms/macos.md
+++ /dev/null
@@ -1,52 +0,0 @@
----
-title: 'macOS'
----
-
-# macOS builds
-
-## Pre-requisites
-
-Pre-requisite: you need to have native build tools installed.
-
-Because the builds are happening without full isolation, there might be some differences compared to CI builds (Xcode version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
-
-In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. `Cache folder: /Users/Matt/Library/Caches/cibuildwheel`).
-
-You can override the cache folder using the `CIBW_CACHE_PATH` environment variable.
-
-!!! warning
-    cibuildwheel uses official python.org macOS installers for CPython but those can only be installed globally.
-
-    In order not to mess with your system, cibuildwheel won't install those if they are missing. Instead, it will error out with a message to let you install the missing CPython:
-
-    ```console
-    Error: CPython 3.9 is not installed.
-    cibuildwheel will not perform system-wide installs when running outside of CI.
-    To build locally, install CPython 3.9 on this machine, or, disable this version of Python using CIBW_SKIP=cp39-macosx_*
-
-    Download link: https://www.python.org/ftp/python/3.9.8/python-3.9.8-macosx10.9.pkg
-    ```
-
-## macOS Version Compatibility
-
-macOS builds will honor the `MACOSX_DEPLOYMENT_TARGET` environment variable to control the minimum supported macOS version for generated wheels. The lowest value you can set `MACOSX_DEPLOYMENT_TARGET` is as follows:
-
-| Arch  | Python version range | Minimum target |
-|-------|----------------------|----------------|
-| Intel | CPython 3.8-3.11     | 10.9           |
-| Intel | CPython 3.12+        | 10.13          |
-| AS    | CPython or PyPy      | 11             |
-| Intel | PyPy 3.8             | 10.13          |
-| Intel | PyPy 3.9+            | 10.15          |
-
-If you set the value lower, cibuildwheel will cap it to the lowest supported value for each target as needed.
-
-!!! note
-    For Rust-based extensions, `Rustc` requires `MACOSX_DEPLOYMENT_TARGET` to be at
-    least 10.12. However, `cibuildwheel` defaults to 10.9 for
-    **Intel / CPython 3.8-3.11** builds. Users must manually set
-    `MACOSX_DEPLOYMENT_TARGET` to 10.12 or higher when building Rust extensions.
-
-## Universal builds
-
-By default, macOS builds will build a single architecture wheel, using the build machine's architecture. If you need to support both x86_64 and Apple Silicon, you can use the `CIBW_ARCHS` environment variable to specify the architectures you want to build, or the value `universal2` to build a multi-architecture wheel. cibuildwheel will test x86_64 wheels (or the x86_64 slice of a `universal2` wheel) when running on Apple Silicon hardware, but it is *not* possible to test Apple Silicon wheels on x86_64 hardware.
diff --git a/docs/platforms/pyodide.md b/docs/platforms/pyodide.md
deleted file mode 100644
index a6edad681..000000000
--- a/docs/platforms/pyodide.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-title: 'Pyodide'
----
-
-# Pyodide (WebAssembly) builds (experimental)
-
-## Prerequisites
-
-You need to have a matching host version of Python (unlike all other cibuildwheel platforms). Linux host highly recommended; macOS hosts may work (e.g. invoking `pytest` directly in [`CIBW_TEST_COMMAND`](../options.md#test-command) is [currently failing](https://github.com/pyodide/pyodide/issues/4802)) and Windows hosts will not work.
-
-## Specifying a pyodide build
-
-You must target pyodide with `--platform pyodide` (or use `--only` on the identifier).
diff --git a/docs/platforms/windows.md b/docs/platforms/windows.md
deleted file mode 100644
index 421fa8543..000000000
--- a/docs/platforms/windows.md
+++ /dev/null
@@ -1,15 +0,0 @@
----
-title: 'Windows'
----
-
-# Windows builds
-
-## Pre-requisites
-
-You must have native build tools (i.e., Visual Studio) installed.
-
-Because the builds are happening without full isolation, there might be some differences compared to CI builds (Visual Studio version, OS version, local files, ...) that might prevent you from finding an issue only seen in CI.
-
-In order to speed-up builds, cibuildwheel will cache the tools it needs to be reused for future builds. The folder used for caching is system/user dependent and is reported in the printed preamble of each run (e.g. `Cache folder: C:\Users\Matt\AppData\Local\pypa\cibuildwheel\Cache`).
-
-You can override the cache folder using the ``CIBW_CACHE_PATH`` environment variable.
diff --git a/docs/setup.md b/docs/setup.md
index e82f5a3e8..c13fb972f 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -1,36 +1,29 @@
 ---
-title: 'Setup'
+title: 'Getting started'
 ---
 
-# Setup
+# Getting started
 
-## Platform support
-
-Each platform that cibuildwheel supports has its own prerequisites and platform-specific behaviors. cibuildwheel supports the following platforms:
-
-* [Linux](./platforms/linux.md)
-* [Windows](./platforms/windows.md)
-* [macOS](./platforms/macos.md)
-* [iOS](./platforms/ios.md)
-* [Experimental: Pyodide (WebAssembly)](./platforms/pyodide.md)
-
-## Run cibuildwheel locally (optional) {: #local}
-
-Before getting to CI setup, it can be convenient to test cibuildwheel
-locally to quickly iterate and track down issues without even touching CI.
+Before getting to [CI setup](ci-services.md), it can be convenient to test cibuildwheel locally to quickly iterate and track down issues without having to commit each change, push, and then check CI logs.
 
 Install cibuildwheel and run a build like this:
 
 ```sh
-# using pipx (https://github.com/pypa/pipx)
+# run using uv
+uvx cibuildwheel
+
+# or pipx
 pipx run cibuildwheel
 
-# or,
+# or, install it first
 pip install cibuildwheel
 cibuildwheel
 ```
 
-You should see the builds taking place. You can experiment with options using environment variables or pyproject.toml.
+!!!tip
+    You can pass the `--platform linux` option to cibuildwheel to build Linux wheels, even if you're not on Linux. On most machines, the easiest builds to try are the Linux builds. You don't need any software installed except a Docker daemon, such as [Docker Desktop](https://www.docker.com/get-started/). Each platform that cibuildwheel supports has its own system requirements and platform-specific behaviors. See the [platforms page](platforms.md) for details.
+
+You should see the builds taking place. You can experiment with [options](options.md) using environment variables or pyproject.toml.
 
 !!! tab "Environment variables"
 
@@ -55,7 +48,7 @@ You should see the builds taking place. You can experiment with options using en
 
 !!! tab "pyproject.toml"
 
-    If you write your options into [`pyproject.toml`](options.md#configuration-file), you can work on your options locally, and they'll be automatically picked up when running in CI.
+    If you write your options into [`pyproject.toml`](configuration.md#configuration-file), you can work on your options locally, and they'll be automatically picked up when running in CI.
 
     > pyproject.toml
 
@@ -70,182 +63,6 @@ You should see the builds taking place. You can experiment with options using en
     cibuildwheel
     ```
 
-## Configure a CI service
-
-### GitHub Actions [linux/mac/windows] {: #github-actions}
-
-To build Linux, macOS, and Windows wheels using GitHub Actions, create a `.github/workflows/build_wheels.yml` file in your repo.
-
-!!! tab "Action"
-    For GitHub Actions, `cibuildwheel` provides an action you can use. This is
-    concise and enables easier auto updating via GitHub's Dependabot; see
-    [Automatic updates](faq.md#automatic-updates).
-
-    > .github/workflows/build_wheels.yml
-
-    ```yaml
-    {% include "../examples/github-minimal.yml" %}
-    ```
-
-    Use `env:` to pass [build options](options.md) and `with:` to set
-    `package-dir: .`, `output-dir: wheelhouse` and `config-file: ''`
-    locations (those values are the defaults).
-
-!!! tab "pipx"
-    The GitHub Actions runners have pipx installed, so you can easily build in
-    just one line. This is internally how the action works; the main benefit of
-    the action form is easy updates via GitHub's Dependabot.
-
-    > .github/workflows/build_wheels.yml
-
-    ```yaml
-    {% include "../examples/github-pipx.yml" %}
-    ```
-
-!!! tab "Generic"
-    This is the most generic form using setup-python and pip; it looks the most
-    like the other CI examples. If you want to avoid having setup that takes
-    advantage of GitHub Actions features or pipx being preinstalled, this might
-    appeal to you.
-
-    > .github/workflows/build_wheels.yml
-    {%
-       include-markdown "../README.md"
-       start="<!--generic-github-start-->"
-       end="<!--generic-github-end-->"
-    %}
-
-Commit this file, and push to GitHub - either to your default branch, or to a PR branch. The build should start automatically.
-
-For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
-
-[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example to include iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI.
-
-
-### Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
-
-To build Linux, Mac, and Windows wheels on Azure Pipelines, create a `azure-pipelines.yml` file in your repo.
-
-> azure-pipelines.yml
-
-```yaml
-{% include "../examples/azure-pipelines-minimal.yml" %}
-```
-
-Commit this file, enable building of your repo on Azure Pipelines, and push.
-
-Wheels will be stored for you and available through the Pipelines interface. For more info on this file, check out the [docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema).
-
-### Travis CI [linux/windows] {: #travis-ci}
-
-To build Linux and Windows wheels on Travis CI, create a `.travis.yml` file in your repo.
-
-> .travis.yml
-
-```yaml
-{% include "../examples/travis-ci-minimal.yml" %}
-```
-
-Commit this file, enable building of your repo on Travis CI, and push.
-
-Then setup a deployment method by following the [Travis CI deployment docs](https://docs.travis-ci.com/user/deployment/), or see [Delivering to PyPI](deliver-to-pypi.md). For more info on `.travis.yml`, check out the [docs](https://docs.travis-ci.com/).
-
-[`examples/travis-ci-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/travis-ci-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
-
-### AppVeyor [linux/mac/windows] {: #appveyor}
-
-To build Linux, Mac, and Windows wheels on AppVeyor, create an `appveyor.yml` file in your repo.
-
-> appveyor.yml
-
-```yaml
-{% include "../examples/appveyor-minimal.yml" %}
-```
-
-Commit this file, enable building of your repo on AppVeyor, and push.
-
-AppVeyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [AppVeyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](deliver-to-pypi.md) below.
-
-For more info on this config file, check out the [docs](https://www.appveyor.com/docs/).
-
-### CircleCI [linux/mac] {: #circleci}
-
-To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file in your repo,
-
-> .circleci/config.yml
-
-```yaml
-{% include "../examples/circleci-minimal.yml" %}
-```
-
-Commit this file, enable building of your repo on CircleCI, and push.
-
-!!! note
-    CircleCI doesn't enable free macOS containers for open source by default, but you can ask for access. See [here](https://circleci.com/docs/2.0/oss/#overview) for more information.
-
-CircleCI will store the built wheels for you - you can access them from the project console. Check out the CircleCI [docs](https://circleci.com/docs/2.0/configuration-reference/#section=configuration) for more info on this config file.
-
-### Gitlab CI [linux] {: #gitlab-ci}
-
-To build Linux wheels on Gitlab CI, create a `.gitlab-ci.yml` file in your repo,
-
-> .gitlab-ci.yml
-
-```yaml
-{% include "../examples/gitlab-minimal.yml" %}
-```
-
-Commit this file, and push to Gitlab. The pipeline should start automatically.
-
-Gitlab will store the built wheels for you - you can access them from the Pipelines view. Check out the Gitlab [docs](https://docs.gitlab.com/ee/ci/yaml/) for more info on this config file.
-
-### Cirrus CI [linux/mac/windows] {: #cirrus-ci}
-
-To build Linux, Mac, and Windows wheels on Cirrus CI, create a `.cirrus.yml` file in your repo,
-
-> .cirrus.yml
-
-```yaml
-{% include "../examples/cirrus-ci-minimal.yml" %}
-```
-
-Commit this file, enable building of your repo on Cirrus CI, and push.
-
-Cirrus CI will store the built wheels for you - you can access them from the individual task view. Check out the Cirrus CI [docs](https://cirrus-ci.org/guide/writing-tasks/) for more info on this config file.
-
-> ⚠️ Got an error? Check the [FAQ](faq.md).
-
-## Next steps
-
-Once you've got the wheel building successfully, you might want to set up [testing](options.md#test-command) or [automatic releases to PyPI](deliver-to-pypi.md#automatic-method).
-
-<script>
-  document.addEventListener('DOMContentLoaded', function() {
-    $('.toctree-l3>a, .rst-content h3').each(function(i, el) {
-      var text = $(el).text()
-      var match = text.match(/(.*) \[([a-z/]+)\]/);
-
-      if (match) {
-        var iconHTML = $.map(match[2].split('/'), function(ident) {
-          switch (ident) {
-            case 'linux':
-              return '<i class="fa fa-linux" aria-hidden="true"></i>'
-            case 'windows':
-              return '<i class="fa fa-windows" aria-hidden="true"></i>'
-            case 'mac':
-              return '<i class="fa fa-apple" aria-hidden="true"></i>'
-          }
-        }).join(' ');
-
-        $(el).append(
-          $('<div>')
-            .append(iconHTML)
-            .css({float: 'right'})
-        )
-        $(el).contents()
-          .filter(function(){ return this.nodeType == 3; }).first()
-          .replaceWith(match[1]);
-      }
-    });
-  });
-</script>
+- Once you've got a build working locally, you can move on to [setting up a CI service](ci-services.md).
+- View the [full options reference](options.md) to see what cibuildwheel can do.
+- Check out the [FAQ](faq.md) for common questions.
diff --git a/mkdocs.yml b/mkdocs.yml
index 1d0150736..b434ca1a1 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -20,20 +20,18 @@ extra_javascript:
 
 nav:
   - Home: index.md
-  - User guide:
+  - Guide:
     - setup.md
-    - options.md
+    - ci-services.md
     - deliver-to-pypi.md
-    - cpp_standards.md
     - faq.md
+    - cpp_standards.md
+  - Reference:
+    - platforms.md
+    - configuration.md
+    - options.md
     - working-examples.md
-  - Supported Platforms:
-    - platforms/linux.md
-    - platforms/windows.md
-    - platforms/macos.md
-    - platforms/ios.md
-    - platforms/pyodide.md
-  - About the project:
+  - About:
     - contributing.md
     - changelog.md
 
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
index 23cd3de64..ec529849d 100644
--- a/test/test_abi_variants.py
+++ b/test/test_abi_variants.py
@@ -180,7 +180,7 @@ def test_abi_none(tmp_path, capfd):
         project_dir,
         add_env={
             "CIBW_TEST_REQUIRES": "pytest",
-            "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} {{project}}/test",
+            "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} ./test",
             # limit the number of builds for test performance reasons
             "CIBW_BUILD": "cp38-* cp{}{}-* cp313t-* pp310-*".format(*utils.SINGLE_PYTHON_VERSION),
         },
diff --git a/test/test_before_test.py b/test/test_before_test.py
index 8a33a8082..7e766abaa 100644
--- a/test/test_before_test.py
+++ b/test/test_before_test.py
@@ -62,8 +62,8 @@ def test(tmp_path, build_frontend_env):
             "CIBW_TEST_REQUIRES": "pytest",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            "CIBW_TEST_COMMAND_WINDOWS": "pytest {project}/test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "pytest ./test",
             **build_frontend_env,
         },
     )
diff --git a/test/test_emulation.py b/test/test_emulation.py
index 70242a510..4bdc0a315 100644
--- a/test/test_emulation.py
+++ b/test/test_emulation.py
@@ -32,7 +32,7 @@ def test(tmp_path, request):
         project_dir,
         add_env={
             "CIBW_TEST_REQUIRES": "pytest",
-            "CIBW_TEST_COMMAND": "pytest {project}/test",
+            "CIBW_TEST_COMMAND": "pytest ./test",
             "CIBW_ARCHS": archs,
         },
     )
diff --git a/test/test_testing.py b/test/test_testing.py
index a4bae870c..16f00aa60 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -80,8 +80,8 @@ def test(tmp_path):
             "CIBW_TEST_REQUIRES": "pytest",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest ./test",
         },
     )
 
@@ -101,8 +101,8 @@ def test_extras_require(tmp_path):
             "CIBW_TEST_EXTRAS": "test",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest ./test",
         },
         single_python=True,
     )
@@ -133,8 +133,8 @@ def test_dependency_groups(tmp_path):
             "CIBW_TEST_GROUPS": "dev",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest ./test",
         },
         single_python=True,
     )
@@ -166,7 +166,7 @@ def test_failing_test(tmp_path):
             output_dir=output_dir,
             add_env={
                 "CIBW_TEST_REQUIRES": "pytest",
-                "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} {{project}}/test",
+                "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} ./test",
                 # CPython 3.8 when running on macOS arm64 is unusual. The build
                 # always runs in x86_64, so the arm64 tests are not run. See
                 # #1169 for reasons why. That means the build succeeds, which

From 625240d4ff4b86d45537939360e3433309d71362 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 14 Apr 2025 17:11:26 -0400
Subject: [PATCH 042/193] [pre-commit.ci] pre-commit autoupdate (#2360)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.4 → v0.11.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.4...v0.11.5)
- [github.com/python-jsonschema/check-jsonschema: 0.32.1 → 0.33.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.32.1...0.33.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 75e2789a0..11e7e982a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.4
+  rev: v0.11.5
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
@@ -81,7 +81,7 @@ repos:
 
 
 - repo: https://github.com/python-jsonschema/check-jsonschema
-  rev: 0.32.1
+  rev: 0.33.0
   hooks:
     - id: check-dependabot
     - id: check-github-actions

From 1fcca39f6da83fbf6d1d9568fb08cccfab99b6cc Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 14 Apr 2025 17:13:04 -0400
Subject: [PATCH 043/193] [Bot] Update dependencies (#2359)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   | 36 ++++++-------
 .../resources/constraints-pyodide312.txt      | 10 ++--
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python38.txt        |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 docs/working-examples.md                      |  8 +--
 11 files changed, 61 insertions(+), 61 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 18838f15c..d8bf3833c 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -112,15 +112,15 @@ python_configurations = [
   { identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
   { identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
   { identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
-  { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.9/python-3.12.9-macos11.pkg" },
-  { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.9/python-3.12.9-macos11.pkg" },
-  { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.9/python-3.12.9-macos11.pkg" },
-  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
+  { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
+  { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
+  { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
+  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
   { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
   { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
   { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
@@ -141,18 +141,18 @@ python_configurations = [
   { identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
   { identifier = "cp311-win32", version = "3.11.9", arch = "32" },
   { identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
-  { identifier = "cp312-win32", version = "3.12.9", arch = "32" },
-  { identifier = "cp312-win_amd64", version = "3.12.9", arch = "64" },
-  { identifier = "cp313-win32", version = "3.13.2", arch = "32" },
-  { identifier = "cp313t-win32", version = "3.13.2", arch = "32" },
-  { identifier = "cp313-win_amd64", version = "3.13.2", arch = "64" },
-  { identifier = "cp313t-win_amd64", version = "3.13.2", arch = "64" },
+  { identifier = "cp312-win32", version = "3.12.10", arch = "32" },
+  { identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" },
+  { identifier = "cp313-win32", version = "3.13.3", arch = "32" },
+  { identifier = "cp313t-win32", version = "3.13.3", arch = "32" },
+  { identifier = "cp313-win_amd64", version = "3.13.3", arch = "64" },
+  { identifier = "cp313t-win_amd64", version = "3.13.3", arch = "64" },
   { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
-  { identifier = "cp312-win_arm64", version = "3.12.9", arch = "ARM64" },
-  { identifier = "cp313-win_arm64", version = "3.13.2", arch = "ARM64" },
-  { identifier = "cp313t-win_arm64", version = "3.13.2", arch = "ARM64" },
+  { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
+  { identifier = "cp313-win_arm64", version = "3.13.3", arch = "ARM64" },
+  { identifier = "cp313t-win_arm64", version = "3.13.3", arch = "ARM64" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index aed02adf7..2a0bfbce7 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -27,7 +27,7 @@ filelock==3.18.0
     # via virtualenv
 h11==0.14.0
     # via httpcore
-httpcore==1.0.7
+httpcore==1.0.8
     # via httpx
 httpx==0.28.1
     # via unearth
@@ -52,7 +52,7 @@ pip==25.0.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
 platformdirs==4.3.7
     # via virtualenv
-pydantic==2.11.2
+pydantic==2.11.3
     # via
     #   pyodide-build
     #   pyodide-lock
@@ -92,7 +92,7 @@ typer==0.15.2
     #   auditwheel-emscripten
     #   pyodide-build
     #   pyodide-cli
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via
     #   anyio
     #   pydantic
@@ -101,9 +101,9 @@ typing-extensions==4.13.1
     #   typing-inspection
 typing-inspection==0.4.0
     # via pydantic
-unearth==0.17.3
+unearth==0.17.5
     # via pyodide-build
-urllib3==2.3.0
+urllib3==2.4.0
     # via requests
 virtualenv==20.30.0
     # via
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 49a2b619f..da66890e6 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 524d961e2..ec5abaa74 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 524d961e2..ec5abaa74 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 524d961e2..ec5abaa74 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 9ea26b911..f5d8ee8af 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 49a2b619f..da66890e6 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 524d961e2..ec5abaa74 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.1
+typing-extensions==4.13.2
     # via delocate
 virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 03e412bea..66cfda9e7 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,45 +1,45 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.05-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.05-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.05-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.12-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.05-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.12-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.05-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.05-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.05-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.12-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.05-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.05-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.05-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.12-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.05-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.05-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.05-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.12-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.05-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.05-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.12-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.12-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.05-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.05-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.05-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.12-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.05-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.05-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.12-1
 
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 1db276593..63574dd2d 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -56,8 +56,8 @@ title: Working examples
 | [envd][]                          | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A machine learning development environment build tool |
 | [Psycopg 3][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A modern implementation of a PostgreSQL adapter for Python |
 | [OpenColorIO][]                   | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. |
-| [aioquic][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | QUIC and HTTP/3 implementation in Python |
 | [ruptures][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Extensive Cython + NumPy [pyproject.toml](https://github.com/deepcharles/ruptures/blob/master/pyproject.toml) example. |
+| [aioquic][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | QUIC and HTTP/3 implementation in Python |
 | [SimpleJSON][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | simplejson is a simple, fast, extensible JSON encoder/decoder for Python  |
 | [OpenTimelineIO][]                | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Open Source API and interchange format for editorial timeline information. |
 | [PyTables][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python package to manage extremely large amounts of data |
@@ -80,8 +80,8 @@ title: Working examples
 | [sourmash][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
 | [abess][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
 | [python-snappy][]                 | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library |
-| [cyvcf2][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
 | [jq.py][]                         | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
+| [cyvcf2][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
 | [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [Tokenizer][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
 | [iminuit][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
@@ -166,8 +166,8 @@ title: Working examples
 [envd]: https://github.com/tensorchord/envd
 [Psycopg 3]: https://github.com/psycopg/psycopg
 [OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO
-[aioquic]: https://github.com/aiortc/aioquic
 [ruptures]: https://github.com/deepcharles/ruptures
+[aioquic]: https://github.com/aiortc/aioquic
 [SimpleJSON]: https://github.com/simplejson/simplejson
 [OpenTimelineIO]: https://github.com/AcademySoftwareFoundation/OpenTimelineIO
 [PyTables]: https://github.com/PyTables/PyTables
@@ -190,8 +190,8 @@ title: Working examples
 [sourmash]: https://github.com/sourmash-bio/sourmash
 [abess]: https://github.com/abess-team/abess
 [python-snappy]: https://github.com/intake/python-snappy
-[cyvcf2]: https://github.com/brentp/cyvcf2
 [jq.py]: https://github.com/mwilliamson/jq.py
+[cyvcf2]: https://github.com/brentp/cyvcf2
 [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [Tokenizer]: https://github.com/OpenNMT/Tokenizer
 [iminuit]: https://github.com/scikit-hep/iminuit

From e9fae06abea5aa7d711cd5cce7aefd9f1e690a05 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 14 Apr 2025 23:20:08 +0200
Subject: [PATCH 044/193] chore: rework virtual environments creation for tests
 (#2356)

As mentioned in https://github.com/pypa/cibuildwheel/pull/2347#discussion_r2029821701

Rework the way test venvs are created to reuse the same mechanisms as build venvs.
---
 cibuildwheel/platforms/macos.py   | 68 +++++++------------------------
 cibuildwheel/platforms/windows.py | 53 +++++-------------------
 cibuildwheel/venv.py              | 28 +++++++++----
 3 files changed, 45 insertions(+), 104 deletions(-)

diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 428971263..4b4a064d8 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -241,25 +241,6 @@ def setup_python(
     # we version pip ourselves, so we don't care about pip version checking
     env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
 
-    # upgrade pip to the version matching our constraints
-    # if necessary, reinstall it to ensure that it's available on PATH as 'pip'
-    if build_frontend == "build[uv]":
-        assert uv_path is not None
-        pip = [str(uv_path), "pip"]
-    else:
-        pip = ["python", "-m", "pip"]
-
-    if not use_uv:
-        call(
-            *pip,
-            "install",
-            "--upgrade",
-            "pip",
-            *constraint_flags(dependency_constraint),
-            env=env,
-            cwd=venv_path,
-        )
-
     # Apply our environment after pip is ready
     env = environment.as_dictionary(prev_environment=env)
 
@@ -434,8 +415,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 build_options.environment,
                 build_frontend.name,
             )
-            if not use_uv:
-                pip_version = get_pip_version(env)
+            pip_version = None if use_uv else get_pip_version(env)
 
             compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
             if compatible_wheel:
@@ -460,7 +440,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 )
 
                 build_env = env.copy()
-                if not use_uv:
+                if pip_version is not None:
                     build_env["VIRTUALENV_PIP"] = pip_version
                 if constraints_path:
                     combine_constraints(
@@ -614,19 +594,6 @@ def build(options: Options, tmp_path: Path) -> None:
                         else f"Testing wheel on {testing_arch}..."
                     )
 
-                    # set up a virtual environment to install and test from, to make sure
-                    # there are no dependencies that were pulled in at build time.
-                    if not use_uv:
-                        call(
-                            "pip",
-                            "install",
-                            "virtualenv",
-                            *constraint_flags(constraints_path),
-                            env=env,
-                        )
-
-                    venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
-
                     arch_prefix = []
                     uv_arch_args = []
                     if testing_arch != machine_arch:
@@ -642,29 +609,24 @@ def build(options: Options, tmp_path: Path) -> None:
                     call_with_arch = functools.partial(call, *arch_prefix)
                     shell_with_arch = functools.partial(call, *arch_prefix, "/bin/sh", "-c")
 
+                    # set up a virtual environment to install and test from, to make sure
+                    # there are no dependencies that were pulled in at build time.
+                    venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
+                    virtualenv_env = virtualenv(
+                        config.version,
+                        base_python,
+                        venv_dir,
+                        None,
+                        use_uv=use_uv,
+                        env=env,
+                        pip_version=pip_version,
+                    )
                     if use_uv:
                         pip_install = functools.partial(call, *pip, "install", *uv_arch_args)
-                        call("uv", "venv", venv_dir, f"--python={base_python}", env=env)
                     else:
                         pip_install = functools.partial(call_with_arch, *pip, "install")
-                        # Use pip version from the initial env to ensure determinism
-                        venv_args = [
-                            "--no-periodic-update",
-                            f"--pip={pip_version}",
-                            "--no-setuptools",
-                            "--no-wheel",
-                        ]
-                        call_with_arch("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
-
-                    virtualenv_env = env.copy()
+
                     virtualenv_env["MACOSX_DEPLOYMENT_TARGET"] = get_test_macosx_deployment_target()
-                    virtualenv_env["PATH"] = os.pathsep.join(
-                        [
-                            str(venv_dir / "bin"),
-                            virtualenv_env["PATH"],
-                        ]
-                    )
-                    virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
 
                     # check that we are using the Python from the virtual environment
                     call_with_arch("which", "python", env=virtualenv_env)
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index e429015b9..a63890644 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -265,21 +265,6 @@ def setup_python(
     env["PYTHON_ARCH"] = python_configuration.arch
     env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
 
-    # upgrade pip to the version matching our constraints
-    # if necessary, reinstall it to ensure that it's available on PATH as 'pip.exe'
-    if not use_uv:
-        call(
-            "python",
-            "-m",
-            "pip",
-            "install",
-            "--upgrade",
-            "pip",
-            *constraint_flags(dependency_constraint),
-            env=env,
-            cwd=venv_path,
-        )
-
     # update env with results from CIBW_ENVIRONMENT
     env = environment.as_dictionary(prev_environment=env)
 
@@ -378,8 +363,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 build_options.environment,
                 build_frontend.name,
             )
-            if not use_uv:
-                pip_version = get_pip_version(env)
+            pip_version = None if use_uv else get_pip_version(env)
 
             compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
             if compatible_wheel:
@@ -407,7 +391,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 )
 
                 build_env = env.copy()
-                if not use_uv:
+                if pip_version is not None:
                     build_env["VIRTUALENV_PIP"] = pip_version
 
                 if constraints_path:
@@ -486,33 +470,16 @@ def build(options: Options, tmp_path: Path) -> None:
                 log.step("Testing wheel...")
                 # set up a virtual environment to install and test from, to make sure
                 # there are no dependencies that were pulled in at build time.
-                if not use_uv:
-                    call(
-                        "pip", "install", "virtualenv", *constraint_flags(constraints_path), env=env
-                    )
-
                 venv_dir = identifier_tmp_dir / "venv-test"
-
-                if use_uv:
-                    call("uv", "venv", venv_dir, f"--python={base_python}", env=env)
-                else:
-                    # Use pip version from the initial env to ensure determinism
-                    venv_args = [
-                        "--no-periodic-update",
-                        f"--pip={pip_version}",
-                        "--no-setuptools",
-                        "--no-wheel",
-                    ]
-                    call("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
-
-                virtualenv_env = env.copy()
-                virtualenv_env["PATH"] = os.pathsep.join(
-                    [
-                        str(venv_dir / "Scripts"),
-                        virtualenv_env["PATH"],
-                    ]
+                virtualenv_env = virtualenv(
+                    config.version,
+                    base_python,
+                    venv_dir,
+                    None,
+                    use_uv=use_uv,
+                    env=env,
+                    pip_version=pip_version,
                 )
-                virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
 
                 # check that we are using the Python from the virtual environment
                 call("where", "python", env=virtualenv_env)
diff --git a/cibuildwheel/venv.py b/cibuildwheel/venv.py
index b8e9cb789..77624b689 100644
--- a/cibuildwheel/venv.py
+++ b/cibuildwheel/venv.py
@@ -93,6 +93,8 @@ def virtualenv(
     dependency_constraint: Path | None,
     *,
     use_uv: bool,
+    env: dict[str, str] | None = None,
+    pip_version: str | None = None,
 ) -> dict[str, str]:
     """
     Create a virtual environment. If `use_uv` is True,
@@ -109,8 +111,9 @@ def virtualenv(
         call("uv", "venv", venv_path, "--python", python)
     else:
         virtualenv_app = _ensure_virtualenv(version)
-        pip_constraint = _parse_pip_constraint_for_virtualenv(dependency_constraint)
-        additional_flags = [f"--pip={pip_constraint}", "--no-setuptools", "--no-wheel"]
+        if pip_version is None:
+            pip_version = _parse_pip_constraint_for_virtualenv(dependency_constraint)
+        additional_flags = [f"--pip={pip_version}", "--no-setuptools", "--no-wheel"]
 
         # Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
         # environment. The initial cost is a bit higher but reusing is much faster.
@@ -118,7 +121,7 @@ def virtualenv(
         # Requires pip>=19.3 so disabling for "embed" because this means we don't know what's the
         # version of pip that will end-up installed.
         # c.f. https://virtualenv.pypa.io/en/latest/cli_interface.html#section-seeder
-        if not _IS_WIN and pip_constraint != "embed" and Version(pip_constraint) >= Version("19.3"):
+        if not _IS_WIN and pip_version != "embed" and Version(pip_version) >= Version("19.3"):
             additional_flags.append("--symlink-app-data")
 
         call(
@@ -132,12 +135,21 @@ def virtualenv(
             python,
             venv_path,
         )
-
     paths = [str(venv_path), str(venv_path / "Scripts")] if _IS_WIN else [str(venv_path / "bin")]
-    env = os.environ.copy()
-    env["PATH"] = os.pathsep.join([*paths, env["PATH"]])
-    env["VIRTUAL_ENV"] = str(venv_path)
-    return env
+    venv_env = os.environ.copy() if env is None else env.copy()
+    venv_env["PATH"] = os.pathsep.join([*paths, venv_env["PATH"]])
+    venv_env["VIRTUAL_ENV"] = str(venv_path)
+    if not use_uv and pip_version == "embed":
+        call(
+            "pip",
+            "install",
+            "--upgrade",
+            "pip",
+            *constraint_flags(dependency_constraint),
+            env=venv_env,
+            cwd=venv_path,
+        )
+    return venv_env
 
 
 def find_uv() -> Path | None:

From 95e3a6575f777977aa5d3ac9274c6b85fce9c2d3 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 14 Apr 2025 23:20:26 +0200
Subject: [PATCH 045/193] fix(CI): Cirrus CI macos jobs (#2355)

* fix(CI): Cirrus CI macos_arm64_cp38 job

* fix(CI): run on macos-runner:sequoia

* doc: update Cirrus CI minimal example
---
 .cirrus.yml                    | 6 +++---
 examples/cirrus-ci-minimal.yml | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.cirrus.yml b/.cirrus.yml
index 6c13ee1a7..1250dacb7 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -62,7 +62,7 @@ windows_x86_task:
 
 macos_arm64_task:
   macos_instance:
-    image: ghcr.io/cirruslabs/macos-runner:sonoma
+    image: ghcr.io/cirruslabs/macos-runner:sequoia
   env:
     VENV_ROOT: ${HOME}/venv-cibuildwheel
     PATH: ${VENV_ROOT}/bin:${PATH}
@@ -73,11 +73,11 @@ macos_arm64_task:
 
 macos_arm64_cp38_task:
   macos_instance:
-    image: ghcr.io/cirruslabs/macos-runner:sonoma
+    image: ghcr.io/cirruslabs/macos-runner:sequoia
   env:
     VENV_ROOT: ${HOME}/venv-cibuildwheel
     PATH: ${VENV_ROOT}/bin:${PATH}
-    PYTEST_ADDOPTS: --run-cp38-universal2 -k 'test_cp38_arm64_testing_universal2_installer or test_arch_auto'
+    PYTEST_ADDOPTS: --run-cp38-universal2 -k 'test_cp38_arm64_testing_universal2_installer or test_arch_auto or test_dummy_serial'
   install_pre_requirements_script:
     - brew install python@3.12
     - python3.12 -m venv ${VENV_ROOT}
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 59f724c2c..712e964cb 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -60,7 +60,7 @@ windows_x86_task:
 macos_arm64_task:
   name: Build macOS arm64 wheels.
   macos_instance:
-    image: ghcr.io/cirruslabs/macos-runner:sonoma
+    image: ghcr.io/cirruslabs/macos-runner:sequoia
   env:
     VENV_ROOT: ${HOME}/venv-cibuildwheel
     PATH: ${VENV_ROOT}/bin:${PATH}

From 99183d68ac491ca04c562a5de91c7287b1bd1728 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 15 Apr 2025 14:49:17 -0400
Subject: [PATCH 046/193] ci: add Windows ARM (#2362)

* ci: add Windows ARM

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: install rust on Windows ARM

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: different action for rust

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: no sync required (avoid dev group)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: expected wheels on Windows ARM64

* fix: skip python 3.8 on Windows ARM64

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
---
 .github/workflows/test.yml       | 14 +++++++-------
 test/test_dependency_versions.py |  3 +++
 test/utils.py                    | 10 +++++++++-
 unit_test/oci_container_test.py  |  1 +
 4 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 675831734..4f4b3a772 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -36,7 +36,7 @@ jobs:
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-15]
+        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-15]
         python_version: ['3.13']
         include:
         - os: ubuntu-latest
@@ -70,7 +70,7 @@ jobs:
 
     - name: Generate a sample project
       run: |
-        uv run -m test.test_projects test.test_0_basic.basic_project sample_proj
+        uv run --no-sync -m test.test_projects test.test_0_basic.basic_project sample_proj
 
     - name: Run a sample build (GitHub Action)
       uses: ./
@@ -122,7 +122,7 @@ jobs:
 
     - name: Test cibuildwheel
       run: |
-        uv run bin/run_tests.py ${{ (runner.os == 'Linux' && runner.arch == 'X64') && '--run-podman' || '' }}
+        uv run --no-sync bin/run_tests.py ${{ (runner.os == 'Linux' && runner.arch == 'X64') && '--run-podman' || '' }}
 
   emulated-archs:
     name: Get qemu emulated architectures
@@ -141,7 +141,7 @@ jobs:
     - name: Get qemu emulated architectures
       id: archs
       run: |
-        OUTPUT=$(uv run python -c "from json import dumps; from test.utils import EMULATED_ARCHS; print(dumps(EMULATED_ARCHS))")
+        OUTPUT=$(uv run --no-sync python -c "from json import dumps; from test.utils import EMULATED_ARCHS; print(dumps(EMULATED_ARCHS))")
         echo "${OUTPUT}"
         echo "archs=${OUTPUT}" >> "$GITHUB_OUTPUT"
 
@@ -166,7 +166,7 @@ jobs:
       uses: docker/setup-qemu-action@v3
 
     - name: Run the emulation tests
-      run: uv run pytest --run-emulation ${{ matrix.arch }} test/test_emulation.py
+      run: uv run --no-sync pytest --run-emulation ${{ matrix.arch }} test/test_emulation.py
 
   test-pyodide:
     name: Test cibuildwheel building Pyodide wheels
@@ -186,7 +186,7 @@ jobs:
 
     - name: Generate a sample project
       run: |
-        uv run -m test.test_projects test.test_0_basic.basic_project sample_proj
+        uv run --no-sync -m test.test_projects test.test_0_basic.basic_project sample_proj
 
     - name: Run a sample build (GitHub Action)
       uses: ./
@@ -198,6 +198,6 @@ jobs:
 
     - name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
       run: |
-        uv run ./bin/run_tests.py
+        uv run --no-sync ./bin/run_tests.py
       env:
         CIBW_PLATFORM: pyodide
diff --git a/test/test_dependency_versions.py b/test/test_dependency_versions.py
index b264868f3..b8d80f3bc 100644
--- a/test/test_dependency_versions.py
+++ b/test/test_dependency_versions.py
@@ -1,3 +1,4 @@
+import platform
 import re
 import textwrap
 from pathlib import Path
@@ -56,6 +57,8 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv):
         pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
     if python_version != "3.12" and utils.platform == "pyodide":
         pytest.skip(f"pyodide does not support Python {python_version}")
+    if python_version == "3.8" and utils.platform == "windows" and platform.machine() == "ARM64":
+        pytest.skip(f"Windows ARM64 does not support Python {python_version}")
 
     project_dir = tmp_path / "project"
     project_with_expected_version_checks.generate(project_dir)
diff --git a/test/utils.py b/test/utils.py
index d999e1b40..b3d2b172d 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -245,6 +245,10 @@ def _expected_wheels(
             "cp313-cp313t",
         ]
 
+        if machine_arch == "ARM64":
+            # no CPython 3.8 on Windows ARM64
+            python_abi_tags.pop(0)
+
         if machine_arch in ["x86_64", "i686", "AMD64", "aarch64", "arm64"]:
             python_abi_tags += [
                 "pp38-pypy38_pp73",
@@ -290,7 +294,11 @@ def _expected_wheels(
                 )
 
         elif platform == "windows":
-            platform_tags = ["win_amd64"] if machine_arch == "AMD64" else ["win32"]
+            platform_tags = {
+                "AMD64": ["win_amd64"],
+                "ARM64": ["win_arm64"],
+                "x86": ["win32"],
+            }.get(machine_arch, [])
 
         elif platform == "macos":
             if python_abi_tag.startswith("pp"):
diff --git a/unit_test/oci_container_test.py b/unit_test/oci_container_test.py
index 5acc9cc48..24466023e 100644
--- a/unit_test/oci_container_test.py
+++ b/unit_test/oci_container_test.py
@@ -37,6 +37,7 @@
     "s390x": OCIPlatform.S390X,
     "aarch64": OCIPlatform.ARM64,
     "arm64": OCIPlatform.ARM64,
+    "ARM64": OCIPlatform.ARM64,
 }[pm]
 
 PODMAN = OCIContainerEngineConfig(name="podman")

From 03f1905bb9c49fa8409c860a4d89001d6a402da8 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 21 Apr 2025 22:45:23 +0100
Subject: [PATCH 047/193] [pre-commit.ci] pre-commit autoupdate (#2370)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.5 → v0.11.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.5...v0.11.6)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 11e7e982a..6e0fec079 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.5
+  rev: v0.11.6
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 4e5b8ee8e802e93138d3a236a81191509298a9d6 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Wed, 23 Apr 2025 09:36:36 -0400
Subject: [PATCH 048/193] [Bot] Update dependencies (#2368)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |  2 +-
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python38.txt        |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 9 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 2a0bfbce7..e7bd14b30 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -42,7 +42,7 @@ markdown-it-py==3.0.0
     # via rich
 mdurl==0.1.2
     # via markdown-it-py
-packaging==24.2
+packaging==25.0
     # via
     #   auditwheel-emscripten
     #   build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index da66890e6..9eccc27be 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -14,7 +14,7 @@ importlib-metadata==8.6.1
     # via build
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index ec5abaa74..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index ec5abaa74..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index ec5abaa74..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index f5d8ee8af..490e373a9 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -14,7 +14,7 @@ importlib-metadata==8.5.0
     # via build
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index da66890e6..9eccc27be 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -14,7 +14,7 @@ importlib-metadata==8.6.1
     # via build
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index ec5abaa74..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 66cfda9e7..528a4eebd 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,45 +1,45 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.19-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.19-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.19-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.19-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.19-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.19-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.19-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.12-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.19-1
 

From 4241f37b2c5be7f7ed96214b83f8cfbe1496cc28 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sat, 26 Apr 2025 11:35:02 +0100
Subject: [PATCH 049/193] [2.x] Update dependencies (#2371)

---
 cibuildwheel/resources/build-platforms.toml   | 36 ++++++++--------
 .../resources/cibuildwheel.schema.json        |  4 +-
 .../resources/constraints-pyodide312.txt      | 23 +++++-----
 .../resources/constraints-python310.txt       |  6 +--
 .../resources/constraints-python311.txt       |  6 +--
 .../resources/constraints-python312.txt       |  6 +--
 .../resources/constraints-python313.txt       |  6 +--
 .../resources/constraints-python38.txt        |  6 +--
 .../resources/constraints-python39.txt        |  6 +--
 cibuildwheel/resources/constraints.txt        |  6 +--
 .../resources/pinned_docker_images.cfg        | 42 +++++++++----------
 cibuildwheel/resources/virtualenv.toml        |  2 +-
 12 files changed, 76 insertions(+), 73 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index ddf379426..3f27c05fc 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -141,15 +141,15 @@ python_configurations = [
   { identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
   { identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
   { identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
-  { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.9/python-3.12.9-macos11.pkg" },
-  { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.9/python-3.12.9-macos11.pkg" },
-  { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.9/python-3.12.9-macos11.pkg" },
-  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
-  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.2/python-3.13.2-macos11.pkg" },
+  { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
+  { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
+  { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
+  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
   { identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" },
   { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
   { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
@@ -175,18 +175,18 @@ python_configurations = [
   { identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
   { identifier = "cp311-win32", version = "3.11.9", arch = "32" },
   { identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
-  { identifier = "cp312-win32", version = "3.12.9", arch = "32" },
-  { identifier = "cp312-win_amd64", version = "3.12.9", arch = "64" },
-  { identifier = "cp313-win32", version = "3.13.2", arch = "32" },
-  { identifier = "cp313t-win32", version = "3.13.2", arch = "32" },
-  { identifier = "cp313-win_amd64", version = "3.13.2", arch = "64" },
-  { identifier = "cp313t-win_amd64", version = "3.13.2", arch = "64" },
+  { identifier = "cp312-win32", version = "3.12.10", arch = "32" },
+  { identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" },
+  { identifier = "cp313-win32", version = "3.13.3", arch = "32" },
+  { identifier = "cp313t-win32", version = "3.13.3", arch = "32" },
+  { identifier = "cp313-win_amd64", version = "3.13.3", arch = "64" },
+  { identifier = "cp313t-win_amd64", version = "3.13.3", arch = "64" },
   { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
-  { identifier = "cp312-win_arm64", version = "3.12.9", arch = "ARM64" },
-  { identifier = "cp313-win_arm64", version = "3.13.2", arch = "ARM64" },
-  { identifier = "cp313t-win_arm64", version = "3.13.2", arch = "ARM64" },
+  { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
+  { identifier = "cp313-win_arm64", version = "3.13.3", arch = "ARM64" },
+  { identifier = "cp313t-win_arm64", version = "3.13.3", arch = "ARM64" },
   { identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 68c195ab2..e9ef3985b 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -13,10 +13,9 @@
     },
     "enable": {
       "enum": [
-        "cpython-eol",
         "cpython-freethreading",
         "cpython-prerelease",
-        "pypy-eol"
+        "pypy"
       ]
     },
     "description": "A Python version or flavor to enable."
@@ -289,6 +288,7 @@
       "type": "boolean",
       "default": false,
       "description": "The project supports free-threaded builds of Python (PEP703)",
+      "deprecated": "Use the `enable` option instead.",
       "title": "CIBW_FREE_THREADED_SUPPORT"
     },
     "manylinux-aarch64-image": {
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 7827ad07e..e7bd14b30 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -19,7 +19,7 @@ charset-normalizer==3.4.1
     # via requests
 click==8.1.8
     # via typer
-cmake==3.31.6
+cmake==4.0.0
     # via pyodide-build
 distlib==0.3.9
     # via virtualenv
@@ -27,7 +27,7 @@ filelock==3.18.0
     # via virtualenv
 h11==0.14.0
     # via httpcore
-httpcore==1.0.7
+httpcore==1.0.8
     # via httpx
 httpx==0.28.1
     # via unearth
@@ -42,7 +42,7 @@ markdown-it-py==3.0.0
     # via rich
 mdurl==0.1.2
     # via markdown-it-py
-packaging==24.2
+packaging==25.0
     # via
     #   auditwheel-emscripten
     #   build
@@ -52,11 +52,11 @@ pip==25.0.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
 platformdirs==4.3.7
     # via virtualenv
-pydantic==2.10.6
+pydantic==2.11.3
     # via
     #   pyodide-build
     #   pyodide-lock
-pydantic-core==2.27.2
+pydantic-core==2.33.1
     # via pydantic
 pygments==2.19.1
     # via rich
@@ -74,7 +74,7 @@ requests==2.32.3
     # via pyodide-build
 resolvelib==1.1.0
     # via pyodide-build
-rich==13.9.4
+rich==14.0.0
     # via
     #   pyodide-build
     #   pyodide-cli
@@ -92,17 +92,20 @@ typer==0.15.2
     #   auditwheel-emscripten
     #   pyodide-build
     #   pyodide-cli
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via
     #   anyio
     #   pydantic
     #   pydantic-core
     #   typer
-unearth==0.17.3
+    #   typing-inspection
+typing-inspection==0.4.0
+    # via pydantic
+unearth==0.17.5
     # via pyodide-build
-urllib3==2.3.0
+urllib3==2.4.0
     # via requests
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via
     #   build
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index c7cd2fb8e..9eccc27be 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -14,7 +14,7 @@ importlib-metadata==8.6.1
     # via build
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
@@ -26,9 +26,9 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 7b07dba10..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 7b07dba10..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 7b07dba10..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 72784c87f..490e373a9 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -14,7 +14,7 @@ importlib-metadata==8.5.0
     # via build
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
@@ -26,9 +26,9 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.20.2
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index c7cd2fb8e..9eccc27be 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -14,7 +14,7 @@ importlib-metadata==8.6.1
     # via build
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
@@ -26,9 +26,9 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 7b07dba10..7abb00bfa 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -12,7 +12,7 @@ filelock==3.18.0
     # via virtualenv
 macholib==1.16.3
     # via delocate
-packaging==24.2
+packaging==25.0
     # via
     #   build
     #   delocate
@@ -22,7 +22,7 @@ platformdirs==4.3.7
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.12.2
+typing-extensions==4.13.2
     # via delocate
-virtualenv==20.29.3
+virtualenv==20.30.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index b7318293b..ad5f418c0 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,58 +1,58 @@
 [x86_64]
 manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.19-1
 
 [i686]
 manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.19-1
 
 [pypy_x86_64]
 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
 
 [pypy_i686]
 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.19-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.19-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.19-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.19-1
 musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.10.26-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.03.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.19-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.03.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.03.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.03.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.03.23-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.19-1
 
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index b791121a1..09e96e302 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1,2 +1,2 @@
 py36 = { version = "20.21.1", url = "https://github.com/pypa/get-virtualenv/blob/20.21.1/public/virtualenv.pyz?raw=true" }
-default = { version = "20.29.3", url = "https://github.com/pypa/get-virtualenv/blob/20.29.3/public/virtualenv.pyz?raw=true" }
+default = { version = "20.30.0", url = "https://github.com/pypa/get-virtualenv/blob/20.30.0/public/virtualenv.pyz?raw=true" }

From faf86a6ed7efa889faf6996aa23820831055001a Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sat, 26 Apr 2025 11:39:25 +0100
Subject: [PATCH 050/193] Bump version: v2.23.3

---
 README.md                              | 16 +++++++---------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  6 ++++++
 docs/faq.md                            |  6 +++---
 docs/setup.md                          |  4 ++--
 examples/appveyor-minimal.yml          |  2 +-
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 19 files changed, 40 insertions(+), 36 deletions(-)

diff --git a/README.md b/README.md
index 1d8b087df..9a3c0ab24 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==2.23.2
+        run: python -m pip install cibuildwheel==2.23.3
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -215,6 +215,12 @@ Changelog
 
 <!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
 
+### v2.23.3
+
+_26 April 2025_
+
+- 🛠 Dependency updates, including Python 3.13.3 (#2371)
+
 ### v2.23.2
 
 _24 March 2025_
@@ -256,14 +262,6 @@ _23 November 2024_
 - 🛠 Now cibuildwheel uses dependency-groups for development dependencies (#2064, #2085)
 - 📚 Docs updates and tidy ups (#2061, #2067, #2072)
 
-
-### v2.21.3
-
-_9 October 2024_
-
-- 🛠 Update CPython 3.13 to 3.13.0 final release (#2032)
-- 📚 Docs updates and tidy ups (#2035)
-
 <!-- END bin/update_readme_changelog.py -->
 
 ---
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index de14b4cba..222a417b9 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1,3 +1,3 @@
 from __future__ import annotations
 
-__version__ = "2.23.2"
+__version__ = "2.23.3"
diff --git a/docs/changelog.md b/docs/changelog.md
index b9234b943..a633ea807 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,12 @@ title: Changelog
 
 # Changelog
 
+### v2.23.3
+
+_26 April 2025_
+
+- 🛠 Dependency updates, including Python 3.13.3 (#2371)
+
 ### v2.23.2
 
 _24 March 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 86835d6ef..82a6ccc88 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -142,7 +142,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v2.23.2
+uses: pypa/cibuildwheel@v2.23.3
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -164,7 +164,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==2.23.2
+cibuildwheel==2.23.3
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -328,7 +328,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==2.23.2 --output-dir wheelhouse
+pipx run cibuildwheel==2.23.3 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/docs/setup.md b/docs/setup.md
index 29e8a380e..4c743f981 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -161,7 +161,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
           - uses: actions/checkout@v4
 
           - name: Build wheels
-            run: pipx run cibuildwheel==2.23.2
+            run: pipx run cibuildwheel==2.23.3
 
           - uses: actions/upload-artifact@v4
             with:
@@ -198,7 +198,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
           - uses: actions/setup-python@v5
 
           - name: Install cibuildwheel
-            run: python -m pip install cibuildwheel==2.23.2
+            run: python -m pip install cibuildwheel==2.23.3
 
           - name: Build wheels
             run: python -m cibuildwheel --output-dir wheelhouse
diff --git a/examples/appveyor-minimal.yml b/examples/appveyor-minimal.yml
index b9c9e11c0..446c2c912 100644
--- a/examples/appveyor-minimal.yml
+++ b/examples/appveyor-minimal.yml
@@ -9,7 +9,7 @@ environment:
 
 stack: python 3.12
 
-install: python -m pip install cibuildwheel==2.23.2
+install: python -m pip install cibuildwheel==2.23.3
 
 build_script: python -m cibuildwheel --output-dir wheelhouse
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 29c3bc603..db66bfddd 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==2.23.2
+        pip3 install cibuildwheel==2.23.3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==2.23.2
+        python3 -m pip install cibuildwheel==2.23.3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==2.23.2
+        pip install cibuildwheel==2.23.3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 1265b26fb..426cd648b 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.2
+            python3 -m pip install --user cibuildwheel==2.23.3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.2
+            python3 -m pip install --user cibuildwheel==2.23.3
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==2.23.2
+            pip3 install cibuildwheel==2.23.3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 9fe287a42..324f075fb 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.2
+    - python -m pip install cibuildwheel==2.23.3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 59f724c2c..599f3db1e 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.2
+    - python -m pip install cibuildwheel==2.23.3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 5d105a58a..bb4747d5a 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -23,7 +23,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.2
+        uses: pypa/cibuildwheel@v2.23.3
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 7ad9e610d..839100bf2 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.2
+        uses: pypa/cibuildwheel@v2.23.3
         # env:
         #   CIBW_SOME_OPTION: value
         #    ...
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index fd0126a74..3c6ee1ab8 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.2
+        uses: pypa/cibuildwheel@v2.23.3
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index b4268771b..7441a0e63 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==2.23.2
+    - python -m pip install cibuildwheel==2.23.3
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==2.23.2
+    - py -m pip install cibuildwheel==2.23.3
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==2.23.2
+    - python3 -m pip install cibuildwheel==2.23.3
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 36cc5e4cc..2a6bc55ae 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==2.23.2
+    - python -m pip install cibuildwheel==2.23.3
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index 82959ee67..903b54b92 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.2
+  - python3 -m pip install cibuildwheel==2.23.3
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 45aa1c4ee..63c812820 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.2
+  - python3 -m pip install cibuildwheel==2.23.3
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 19a6e1fa8..ae992946c 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==2.23.2 twine
+      install: python3 -m pip install cibuildwheel==2.23.3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==2.23.2 twine
+      install: python3 -m pip install cibuildwheel==2.23.3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index e85583614..b8a180b29 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "2.23.2"
+version = "2.23.3"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 4449d7b5fafdc2111ec2d4b0cf864db1873e6571 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Mon, 28 Apr 2025 09:02:43 -0700
Subject: [PATCH 051/193] chore: type check install_certifi.py (#2365)

---
 .pre-commit-config.yaml                   | 1 -
 cibuildwheel/resources/install_certifi.py | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6e0fec079..fb55aa5bf 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -25,7 +25,6 @@ repos:
   hooks:
   - id: mypy
     name: mypy 3.11 on cibuildwheel/
-    exclude: ^cibuildwheel/resources/.*py$
     args: ["--python-version=3.11"]
     additional_dependencies: &mypy-dependencies
       - bracex
diff --git a/cibuildwheel/resources/install_certifi.py b/cibuildwheel/resources/install_certifi.py
index 0d261d51a..860521750 100644
--- a/cibuildwheel/resources/install_certifi.py
+++ b/cibuildwheel/resources/install_certifi.py
@@ -26,7 +26,7 @@
 )
 
 
-def main():
+def main() -> None:
     openssl_dir, openssl_cafile = os.path.split(ssl.get_default_verify_paths().openssl_cafile)
     print(" -- pip install --upgrade certifi")
     subprocess.check_call(

From 0463d260668617976c029ee2c3cf26b1cae5e71b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 12:02:52 -0400
Subject: [PATCH 052/193] chore(deps): bump astral-sh/setup-uv from 5 to 6 in
 the actions group (#2375)

Bumps the actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).


Updates `astral-sh/setup-uv` from 5 to 6
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v5...v6)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/test.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 4f4b3a772..b6bb7c866 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -50,7 +50,7 @@ jobs:
         python-version: ${{ matrix.python_version }}
         allow-prereleases: true
 
-    - uses: astral-sh/setup-uv@v5
+    - uses: astral-sh/setup-uv@v6
 
     # free some space to prevent reaching GHA disk space limits
     - name: Clean docker images
@@ -135,7 +135,7 @@ jobs:
     - uses: actions/setup-python@v5
       with:
         python-version: "3.x"
-    - uses: astral-sh/setup-uv@v5
+    - uses: astral-sh/setup-uv@v6
     - name: Install dependencies
       run: uv sync --no-dev --group test
     - name: Get qemu emulated architectures
@@ -158,7 +158,7 @@ jobs:
     - uses: actions/setup-python@v5
       with:
         python-version: "3.x"
-    - uses: astral-sh/setup-uv@v5
+    - uses: astral-sh/setup-uv@v6
     - name: Install dependencies
       run: uv sync --no-dev --group test
 
@@ -179,7 +179,7 @@ jobs:
       name: Install Python 3.12
       with:
         python-version: '3.12'
-    - uses: astral-sh/setup-uv@v5
+    - uses: astral-sh/setup-uv@v6
 
     - name: Install dependencies
       run: uv sync --no-dev --group test

From 5f8d06ff55679f81d007fe6aed2e7e0d9e9e3181 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 28 Apr 2025 18:05:03 +0200
Subject: [PATCH 053/193] feat: initial support for riscv64 (#2366)

* feat: initial support for riscv64

* use enable for riscv64

* Apply review suggestion

Co-authored-by: Joe Rickerby <joerick@mac.com>

* update documentation

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 bin/generate_schema.py                        |  7 +++
 bin/update_docker.py                          |  3 +
 cibuildwheel/architecture.py                  |  2 +
 cibuildwheel/logger.py                        |  2 +
 cibuildwheel/oci_container.py                 |  1 +
 cibuildwheel/options.py                       |  2 +
 cibuildwheel/platforms/linux.py               |  1 +
 cibuildwheel/resources/build-platforms.toml   | 14 +++++
 .../resources/cibuildwheel.schema.json        | 60 ++++++++++++++++---
 cibuildwheel/resources/defaults.toml          |  2 +
 .../resources/pinned_docker_images.cfg        |  2 +
 cibuildwheel/selector.py                      |  5 ++
 docs/options.md                               | 34 +++++++----
 test/test_emulation.py                        | 13 +++-
 test/utils.py                                 |  1 +
 unit_test/build_selector_test.py              |  9 +++
 unit_test/main_tests/main_platform_test.py    |  1 +
 unit_test/oci_container_test.py               |  6 +-
 18 files changed, 139 insertions(+), 26 deletions(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index 8ca6a1a76..728a5a56c 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -31,6 +31,7 @@
       - cpython-freethreading
       - cpython-prerelease
       - pypy
+      - cpython-experimental-riscv64
   description: A Python version or flavor to enable.
 additionalProperties: false
 description: cibuildwheel's settings.
@@ -157,6 +158,9 @@
   manylinux-pypy_x86_64-image:
     type: string
     description: Specify alternative manylinux / musllinux container images
+  manylinux-riscv64-image:
+    type: string
+    description: Specify alternative manylinux / musllinux container images
   manylinux-s390x-image:
     type: string
     description: Specify alternative manylinux / musllinux container images
@@ -175,6 +179,9 @@
   musllinux-ppc64le-image:
     type: string
     description: Specify alternative manylinux / musllinux container images
+  musllinux-riscv64-image:
+    type: string
+    description: Specify alternative manylinux / musllinux container images
   musllinux-s390x-image:
     type: string
     description: Specify alternative manylinux / musllinux container images
diff --git a/bin/update_docker.py b/bin/update_docker.py
index 3f23d75da..efb90154e 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -116,5 +116,8 @@ def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None
             suffix = f"_{platform.removeprefix('pypy_')}"
         config[platform][image.manylinux_version] = f"{image.image_name}{suffix}:{tag_name}"
 
+if not config.has_section("riscv64"):
+    config["riscv64"] = {}
+
 with open(RESOURCES / "pinned_docker_images.cfg", "w") as f:
     config.write(f)
diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py
index de17c75dd..0ac2f1a1a 100644
--- a/cibuildwheel/architecture.py
+++ b/cibuildwheel/architecture.py
@@ -51,6 +51,7 @@ class Architecture(StrEnum):
     ppc64le = auto()
     s390x = auto()
     armv7l = auto()
+    riscv64 = auto()
 
     # mac archs
     universal2 = auto()
@@ -167,6 +168,7 @@ def all_archs(platform: PlatformName) -> "set[Architecture]":
                 Architecture.ppc64le,
                 Architecture.s390x,
                 Architecture.armv7l,
+                Architecture.riscv64,
             },
             "macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
             "windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index 391786a04..321352e8c 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -22,12 +22,14 @@
     "manylinux_ppc64le": "manylinux ppc64le",
     "manylinux_s390x": "manylinux s390x",
     "manylinux_armv7l": "manylinux armv7l",
+    "manylinux_riscv64": "manylinux riscv64",
     "musllinux_x86_64": "musllinux x86_64",
     "musllinux_i686": "musllinux i686",
     "musllinux_aarch64": "musllinux aarch64",
     "musllinux_ppc64le": "musllinux ppc64le",
     "musllinux_s390x": "musllinux s390x",
     "musllinux_armv7l": "musllinux armv7l",
+    "musllinux_riscv64": "musllinux riscv64",
     "win32": "Windows 32bit",
     "win_amd64": "Windows 64bit",
     "win_arm64": "Windows on ARM 64bit",
diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py
index 3a4a49958..fa3d77048 100644
--- a/cibuildwheel/oci_container.py
+++ b/cibuildwheel/oci_container.py
@@ -33,6 +33,7 @@ class OCIPlatform(Enum):
     ARMV7 = "linux/arm/v7"
     ARM64 = "linux/arm64"
     PPC64LE = "linux/ppc64le"
+    RISCV64 = "linux/riscv64"
     S390X = "linux/s390x"
 
 
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index b0177e0c9..283f48f7c 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -35,6 +35,7 @@
     "ppc64le",
     "s390x",
     "armv7l",
+    "riscv64",
     "pypy_aarch64",
     "pypy_i686",
 )
@@ -46,6 +47,7 @@
     "ppc64le",
     "s390x",
     "armv7l",
+    "riscv64",
 )
 
 
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 4932298aa..e39a24e08 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -28,6 +28,7 @@
     Architecture.ppc64le: OCIPlatform.PPC64LE,
     Architecture.s390x: OCIPlatform.S390X,
     Architecture.armv7l: OCIPlatform.ARMV7,
+    Architecture.riscv64: OCIPlatform.RISCV64,
 }
 
 
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index d8bf3833c..d2ad15e09 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -46,6 +46,13 @@ python_configurations = [
   { identifier = "cp312-manylinux_armv7l", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp38-manylinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
+  { identifier = "cp39-manylinux_riscv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
+  { identifier = "cp310-manylinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+  { identifier = "cp311-manylinux_riscv64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+  { identifier = "cp312-manylinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
+  { identifier = "cp313-manylinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
+  { identifier = "cp313t-manylinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
   { identifier = "pp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
   { identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
@@ -96,6 +103,13 @@ python_configurations = [
   { identifier = "cp312-musllinux_armv7l", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp38-musllinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
+  { identifier = "cp39-musllinux_ricv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
+  { identifier = "cp310-musllinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
+  { identifier = "cp311-musllinux_riscv64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+  { identifier = "cp312-musllinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
+  { identifier = "cp313-musllinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
+  { identifier = "cp313t-musllinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
 ]
 
 [macos]
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 10e07cc15..46ad0020f 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -15,7 +15,8 @@
       "enum": [
         "cpython-freethreading",
         "cpython-prerelease",
-        "pypy"
+        "pypy",
+        "cpython-experimental-riscv64"
       ]
     },
     "description": "A Python version or flavor to enable."
@@ -357,6 +358,11 @@
       "description": "Specify alternative manylinux / musllinux container images",
       "title": "CIBW_MANYLINUX_PYPY_X86_64_IMAGE"
     },
+    "manylinux-riscv64-image": {
+      "type": "string",
+      "description": "Specify alternative manylinux / musllinux container images",
+      "title": "CIBW_MANYLINUX_RISCV64_IMAGE"
+    },
     "manylinux-s390x-image": {
       "type": "string",
       "description": "Specify alternative manylinux / musllinux container images",
@@ -387,6 +393,11 @@
       "description": "Specify alternative manylinux / musllinux container images",
       "title": "CIBW_MUSLLINUX_PPC64LE_IMAGE"
     },
+    "musllinux-riscv64-image": {
+      "type": "string",
+      "description": "Specify alternative manylinux / musllinux container images",
+      "title": "CIBW_MUSLLINUX_RISCV64_IMAGE"
+    },
     "musllinux-s390x-image": {
       "type": "string",
       "description": "Specify alternative manylinux / musllinux container images",
@@ -566,6 +577,9 @@
               "before-build": {
                 "$ref": "#/$defs/inherit"
               },
+              "xbuild-tools": {
+                "$ref": "#/$defs/inherit"
+              },
               "before-test": {
                 "$ref": "#/$defs/inherit"
               },
@@ -581,9 +595,6 @@
               "environment-pass": {
                 "$ref": "#/$defs/inherit"
               },
-              "xbuild-tools": {
-                "$ref": "#/$defs/inherit"
-              },
               "repair-wheel-command": {
                 "$ref": "#/$defs/inherit"
               },
@@ -652,6 +663,9 @@
           "manylinux-pypy_x86_64-image": {
             "$ref": "#/properties/manylinux-pypy_x86_64-image"
           },
+          "manylinux-riscv64-image": {
+            "$ref": "#/properties/manylinux-riscv64-image"
+          },
           "manylinux-s390x-image": {
             "$ref": "#/properties/manylinux-s390x-image"
           },
@@ -670,12 +684,18 @@
           "musllinux-ppc64le-image": {
             "$ref": "#/properties/musllinux-ppc64le-image"
           },
+          "musllinux-riscv64-image": {
+            "$ref": "#/properties/musllinux-riscv64-image"
+          },
           "musllinux-s390x-image": {
             "$ref": "#/properties/musllinux-s390x-image"
           },
           "musllinux-x86_64-image": {
             "$ref": "#/properties/musllinux-x86_64-image"
           },
+          "xbuild-tools": {
+            "$ref": "#/properties/xbuild-tools"
+          },
           "repair-wheel-command": {
             "$ref": "#/properties/repair-wheel-command"
           },
@@ -752,6 +772,9 @@
         "manylinux-pypy_x86_64-image": {
           "$ref": "#/properties/manylinux-pypy_x86_64-image"
         },
+        "manylinux-riscv64-image": {
+          "$ref": "#/properties/manylinux-riscv64-image"
+        },
         "manylinux-s390x-image": {
           "$ref": "#/properties/manylinux-s390x-image"
         },
@@ -770,12 +793,18 @@
         "musllinux-ppc64le-image": {
           "$ref": "#/properties/musllinux-ppc64le-image"
         },
+        "musllinux-riscv64-image": {
+          "$ref": "#/properties/musllinux-riscv64-image"
+        },
         "musllinux-s390x-image": {
           "$ref": "#/properties/musllinux-s390x-image"
         },
         "musllinux-x86_64-image": {
           "$ref": "#/properties/musllinux-x86_64-image"
         },
+        "xbuild-tools": {
+          "$ref": "#/properties/xbuild-tools"
+        },
         "repair-wheel-command": {
           "description": "Execute a shell command to repair each built wheel.",
           "oneOf": [
@@ -840,6 +869,9 @@
         "environment": {
           "$ref": "#/properties/environment"
         },
+        "xbuild-tools": {
+          "$ref": "#/properties/xbuild-tools"
+        },
         "repair-wheel-command": {
           "$ref": "#/properties/repair-wheel-command"
         },
@@ -891,6 +923,9 @@
         "environment": {
           "$ref": "#/properties/environment"
         },
+        "xbuild-tools": {
+          "$ref": "#/properties/xbuild-tools"
+        },
         "repair-wheel-command": {
           "description": "Execute a shell command to repair each built wheel.",
           "oneOf": [
@@ -955,6 +990,9 @@
         "environment": {
           "$ref": "#/properties/environment"
         },
+        "xbuild-tools": {
+          "$ref": "#/properties/xbuild-tools"
+        },
         "repair-wheel-command": {
           "$ref": "#/properties/repair-wheel-command"
         },
@@ -1006,22 +1044,28 @@
         "environment": {
           "$ref": "#/properties/environment"
         },
-        "repair-wheel-command": {
-          "$ref": "#/properties/repair-wheel-command"
-        },
         "xbuild-tools": {
           "$ref": "#/properties/xbuild-tools"
         },
+        "repair-wheel-command": {
+          "$ref": "#/properties/repair-wheel-command"
+        },
         "test-command": {
           "$ref": "#/properties/test-command"
         },
         "test-extras": {
           "$ref": "#/properties/test-extras"
         },
+        "test-sources": {
+          "$ref": "#/properties/test-sources"
+        },
+        "test-groups": {
+          "$ref": "#/properties/test-groups"
+        },
         "test-requires": {
           "$ref": "#/properties/test-requires"
         }
-       }
+      }
     }
   }
 }
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 8ca424556..4d03394fd 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -33,6 +33,7 @@ manylinux-aarch64-image = "manylinux_2_28"
 manylinux-ppc64le-image = "manylinux_2_28"
 manylinux-s390x-image = "manylinux_2_28"
 manylinux-armv7l-image = "manylinux_2_31"
+manylinux-riscv64-image = "ghcr.io/pypa/cibuildwheel/no_default_image:please_use_override"
 manylinux-pypy_x86_64-image = "manylinux_2_28"
 manylinux-pypy_i686-image = "manylinux2014"
 manylinux-pypy_aarch64-image = "manylinux_2_28"
@@ -43,6 +44,7 @@ musllinux-aarch64-image = "musllinux_1_2"
 musllinux-ppc64le-image = "musllinux_1_2"
 musllinux-s390x-image = "musllinux_1_2"
 musllinux-armv7l-image = "musllinux_1_2"
+musllinux-riscv64-image = "ghcr.io/pypa/cibuildwheel/no_default_image:please_use_override"
 
 
 [tool.cibuildwheel.linux]
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 528a4eebd..7a9962a39 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -43,3 +43,5 @@ manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.19-1
 manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.19-1
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.19-1
 
+[riscv64]
+
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 0e9736f53..98c7a6601 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -32,6 +32,7 @@ class EnableGroup(StrEnum):
     CPythonFreeThreading = "cpython-freethreading"
     CPythonPrerelease = "cpython-prerelease"
     PyPy = "pypy"
+    CPythonExperimentalRiscV64 = "cpython-experimental-riscv64"
 
     @classmethod
     def all_groups(cls) -> frozenset["EnableGroup"]:
@@ -70,6 +71,10 @@ def __call__(self, build_id: str) -> bool:
             return False
         if EnableGroup.PyPy not in self.enable and fnmatch(build_id, "pp*"):
             return False
+        if EnableGroup.CPythonExperimentalRiscV64 not in self.enable and fnmatch(
+            build_id, "*_riscv64"
+        ):
+            return False
 
         should_build = selector_matches(self.build_config, build_id)
         should_skip = selector_matches(self.skip_config, build_id)
diff --git a/docs/options.md b/docs/options.md
index de4317b4d..9d906b0c9 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -50,23 +50,24 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
 
 <div class="build-id-table-marker"></div>
 
-|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                           | iOS                                                                                               |
-|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
-| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l         |                                                                                                   |
-| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l         |                                                                                                   |
-| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l |                                                                                                   |
-| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l |                                                                                                   |
-| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l                             |                                                                                                   |
-| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator |
-| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                |                                                                                                   |
-| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                |                                                                                                   |
-| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                               |                                                                                                   |
-| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                               |                                                                                                   |
+|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                                                                                   | iOS                                                                                               |
+|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
+| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-manylinux_riscv64<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l<br/>cp38-musllinux_riscv64           |                                                                                                   |
+| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-manylinux_riscv64<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l<br/>cp39-musllinux_riscv64           |                                                                                                   |
+| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 |                                                                                                   |
+| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                                                                   |
+| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                                                                   |
+| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator |
+| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |
+| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |
+| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |
+| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |
 
 The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
 The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
 
 Windows arm64 platform support is experimental.
+Linux riscv64 platform support is experimental and requires an explicit opt-in through [CIBW_ENABLE](#enable).
 
 For an experimental WebAssembly build with `--platform pyodide`,
 `cp312-pyodide_wasm32` is the only platform identifier.
@@ -178,7 +179,7 @@ On Windows, this option can be used to [compile for `ARM64` from an Intel machin
 
 Options:
 
-- Linux: `x86_64` `i686` `aarch64` `ppc64le` `s390x` `armv7l`
+- Linux: `x86_64` `i686` `aarch64` `ppc64le` `s390x` `armv7l` `riscv64`
 - macOS: `x86_64` `arm64` `universal2`
 - Windows: `AMD64` `x86` `ARM64`
 - Pyodide: `wasm32`
@@ -191,6 +192,8 @@ Options:
   to use [CIBW_BUILD](#build-skip) with this option to target specific
   architectures via build selectors.
 
+Linux riscv64 platform support is experimental and requires an explicit opt-in through [CIBW_ENABLE](#enable).
+
 Default: `auto`
 
 | Runner | `native` | `auto` | `auto64` | `auto32` |
@@ -317,6 +320,9 @@ values are:
   The build identifiers for those variants have a `t` suffix in their
   `python_tag` (e.g. `cp313t-manylinux_x86_64`).
 - `pypy`: Enable PyPy.
+- `cpython-experimental-riscv64`: Enable experimental riscv64 builds. Those builds
+  are disabled by default as they can't be uploaded to PyPI and a PEP will most likely
+  be required before this can happen.
 
 
 !!! caution
@@ -972,6 +978,7 @@ The available options are:
 | CIBW_MANYLINUX_PPC64LE_IMAGE      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
 | CIBW_MANYLINUX_S390X_IMAGE        | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x)   |
 | CIBW_MANYLINUX_ARMV7L_IMAGE       | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l)  |
+| CIBW_MANYLINUX_RISCV64_IMAGE      | No default                                                      |
 | CIBW_MANYLINUX_PYPY_AARCH64_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
 | CIBW_MANYLINUX_PYPY_I686_IMAGE    | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
 | CIBW_MUSLLINUX_X86_64_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64)    |
@@ -980,6 +987,7 @@ The available options are:
 | CIBW_MUSLLINUX_PPC64LE_IMAGE      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le)   |
 | CIBW_MUSLLINUX_S390X_IMAGE        | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x)     |
 | CIBW_MUSLLINUX_ARMV7L_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l)    |
+| CIBW_MUSLLINUX_RISCV64_IMAGE      | No default                                                      |
 
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
 
diff --git a/test/test_emulation.py b/test/test_emulation.py
index 4bdc0a315..0d218b000 100644
--- a/test/test_emulation.py
+++ b/test/test_emulation.py
@@ -34,14 +34,21 @@ def test(tmp_path, request):
             "CIBW_TEST_REQUIRES": "pytest",
             "CIBW_TEST_COMMAND": "pytest ./test",
             "CIBW_ARCHS": archs,
+            # TODO remove me once proper support is added
+            "CIBW_MANYLINUX_RISCV64_IMAGE": "ghcr.io/mayeut/manylinux_2_31:2025.03.02-1",
+            "CIBW_SKIP": "*-musllinux_riscv64",
         },
     )
 
     # also check that we got the right wheels
-    expected_wheels = itertools.chain.from_iterable(
-        utils.expected_wheels("spam", "0.1.0", machine_arch=arch, single_arch=True)
-        for arch in archs.split(" ")
+    expected_wheels = list(
+        itertools.chain.from_iterable(
+            utils.expected_wheels("spam", "0.1.0", machine_arch=arch, single_arch=True)
+            for arch in archs.split(" ")
+        )
     )
+    # TODO remove me once proper support is added
+    expected_wheels = [wheel for wheel in expected_wheels if "musllinux_1_2_riscv64" not in wheel]
     assert set(actual_wheels) == set(expected_wheels)
 
 
diff --git a/test/utils.py b/test/utils.py
index b3d2b172d..e05d3a2dd 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -227,6 +227,7 @@ def _expected_wheels(
             "armv7l": ["manylinux_2_17", "manylinux2014", "manylinux_2_31"],
             "i686": ["manylinux_2_5", "manylinux1", "manylinux_2_17", "manylinux2014"],
             "x86_64": ["manylinux_2_5", "manylinux1", "manylinux_2_28"],
+            "riscv64": ["manylinux_2_31"],
         }.get(machine_arch, ["manylinux_2_17", "manylinux2014", "manylinux_2_28"])
 
     if musllinux_versions is None:
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index 992677ed9..ea72bf7c2 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -158,6 +158,15 @@ def test_build_free_threaded_python():
     assert build_selector("cp313t-manylinux_x86_64")
 
 
+def test_build_riscv64_enable():
+    build_selector = BuildSelector(build_config="*", skip_config="")
+    assert not build_selector("cp313-manylinux_riscv64")
+    build_selector = BuildSelector(
+        build_config="*", skip_config="", enable=frozenset([EnableGroup.CPythonExperimentalRiscV64])
+    )
+    assert build_selector("cp313-manylinux_riscv64")
+
+
 def test_testing_selector():
     # local import to avoid pytest trying to collect this as a test class!
     from cibuildwheel.selector import TestSelector
diff --git a/unit_test/main_tests/main_platform_test.py b/unit_test/main_tests/main_platform_test.py
index 7ccfab6b8..084207368 100644
--- a/unit_test/main_tests/main_platform_test.py
+++ b/unit_test/main_tests/main_platform_test.py
@@ -179,6 +179,7 @@ def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
             Architecture.ppc64le,
             Architecture.s390x,
             Architecture.armv7l,
+            Architecture.riscv64,
         }
     elif platform == "windows":
         assert options.globals.architectures == {
diff --git a/unit_test/oci_container_test.py b/unit_test/oci_container_test.py
index 24466023e..c6ea3b645 100644
--- a/unit_test/oci_container_test.py
+++ b/unit_test/oci_container_test.py
@@ -552,7 +552,7 @@ def test_local_image(
         # both GHA & local macOS arm64 podman desktop are failing
         pytest.xfail("podman fails with armv7l images")
 
-    remote_image = "debian:12-slim"
+    remote_image = "debian:trixie-slim"
     platform_name = platform.value.replace("/", "_")
     local_image = f"cibw_{container_engine.name}_{platform_name}_local:latest"
     dockerfile = tmp_path / "Dockerfile"
@@ -582,7 +582,7 @@ def test_multiarch_image(container_engine, platform):
         # both GHA & local macOS arm64 podman desktop are failing
         pytest.xfail("podman fails with armv7l images")
     with OCIContainer(
-        engine=container_engine, image="debian:12-slim", oci_platform=platform
+        engine=container_engine, image="debian:trixie-slim", oci_platform=platform
     ) as container:
         output = container.call(["uname", "-m"], capture_output=True)
         output_map_kernel = {
@@ -591,6 +591,7 @@ def test_multiarch_image(container_engine, platform):
             OCIPlatform.ARMV7: ("armv7l", "armv8l"),
             OCIPlatform.ARM64: ("aarch64",),
             OCIPlatform.PPC64LE: ("ppc64le",),
+            OCIPlatform.RISCV64: ("riscv64",),
             OCIPlatform.S390X: ("s390x",),
         }
         assert output.strip() in output_map_kernel[platform]
@@ -601,6 +602,7 @@ def test_multiarch_image(container_engine, platform):
             OCIPlatform.ARMV7: "armhf",
             OCIPlatform.ARM64: "arm64",
             OCIPlatform.PPC64LE: "ppc64el",
+            OCIPlatform.RISCV64: "riscv64",
             OCIPlatform.S390X: "s390x",
         }
         assert output_map_dpkg[platform] == output.strip()

From 020a91baa84b3703bfaf7cf8f8eb084521e7686c Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Tue, 29 Apr 2025 00:25:17 +0200
Subject: [PATCH 054/193] feat: support GraalPy (#1538)

* Properly close files used for testing

* Add support for GraalPy

* Help GraalPy discover build tools on Windows

* Expect manylinux-interpreters ensure graalpy* warning in pip

* Workaround GraalPy bugs on Windows

* Workaround oracle/graalpython#491 also when uv is not available

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update azure-pipelines.yml

* Update azure-pipelines.yml

* refacotor: use pathlib.write_text

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Include GraalPy in docker_warmup and remove workaround for installing it late

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
---
 .github/workflows/test.yml                  |  2 +-
 README.md                                   |  5 +-
 azure-pipelines.yml                         |  3 +-
 bin/update_pythons.py                       | 81 ++++++++++++++++++++-
 cibuildwheel/logger.py                      |  2 +
 cibuildwheel/platforms/macos.py             | 22 +++++-
 cibuildwheel/platforms/windows.py           | 77 ++++++++++++++++++++
 cibuildwheel/resources/build-platforms.toml |  5 ++
 cibuildwheel/selector.py                    |  3 +
 docs/options.md                             |  1 +
 test/conftest.py                            |  3 +-
 test/test_abi_variants.py                   | 10 ++-
 test/test_before_all.py                     |  4 +-
 test/test_before_build.py                   |  4 +-
 test/test_before_test.py                    | 10 ++-
 test/test_dependency_versions.py            | 20 +++++
 test/test_pep518.py                         | 15 ++++
 test/test_testing.py                        | 20 +++--
 test/utils.py                               |  8 +-
 unit_test/linux_build_steps_test.py         |  2 +-
 unit_test/main_tests/main_options_test.py   | 12 +--
 unit_test/option_prepare_test.py            | 16 ++--
 22 files changed, 287 insertions(+), 38 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b6bb7c866..3ae094dcd 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -80,7 +80,7 @@ jobs:
       env:
         CIBW_ARCHS_MACOS: x86_64 universal2 arm64
         CIBW_BUILD_FRONTEND: 'build[uv]'
-        CIBW_ENABLE: "cpython-prerelease cpython-freethreading pypy"
+        CIBW_ENABLE: "cpython-prerelease cpython-freethreading pypy graalpy"
 
     - name: Run a sample build (GitHub Action, only)
       uses: ./
diff --git a/README.md b/README.md
index 12b328001..2d9af6b11 100644
--- a/README.md
+++ b/README.md
@@ -36,14 +36,15 @@ While cibuildwheel itself requires a recent Python version to run (we support th
 | PyPy 3.9 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
 | PyPy 3.10 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
 | PyPy 3.11 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| GraalPy 24.2   | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | N/A  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
 
-<sup>¹ PyPy is only supported for manylinux wheels.</sup><br>
+<sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br>
 <sup>² Windows arm64 support is experimental.</sup><br>
 <sup>³ Free-threaded mode requires opt-in using [`CIBW_ENABLE`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</sup><br>
 <sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br>
 <sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br>
 
-- Builds manylinux, musllinux, macOS 10.9+ (10.13+ for Python 3.12+), and Windows wheels for CPython and PyPy
+- Builds manylinux, musllinux, macOS 10.9+ (10.13+ for Python 3.12+), and Windows wheels for CPython, PyPy, and GraalPy
 - Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
 - Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
 - Runs your library's tests against the wheel-installed version of your library
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index eaf93958e..1cc707fd9 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -6,7 +6,7 @@ pr:
 
 jobs:
 - job: linux_311
-  timeoutInMinutes: 120
+  timeoutInMinutes: 180
   pool: {vmImage: 'Ubuntu-22.04'}
   steps:
     - task: UsePythonVersion@0
@@ -20,6 +20,7 @@ jobs:
 
 - job: macos_311
   pool: {vmImage: 'macOS-13'}
+  timeoutInMinutes: 120
   steps:
     - task: UsePythonVersion@0
       inputs:
diff --git a/bin/update_pythons.py b/bin/update_pythons.py
index 7b8ea1474..75de60a64 100755
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -5,6 +5,7 @@
 import difflib
 import logging
 import operator
+import re
 import tomllib
 from collections.abc import Mapping, MutableMapping
 from pathlib import Path
@@ -44,13 +45,19 @@ class ConfigWinPP(TypedDict):
     url: str
 
 
+class ConfigWinGP(TypedDict):
+    identifier: str
+    version: str
+    url: str
+
+
 class ConfigApple(TypedDict):
     identifier: str
     version: str
     url: str
 
 
-AnyConfig = ConfigWinCP | ConfigWinPP | ConfigApple
+AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple
 
 
 # The following set of "Versions" classes allow the initial call to the APIs to
@@ -106,6 +113,72 @@ def update_version_windows(self, spec: Specifier) -> ConfigWinCP | None:
         )
 
 
+class GraalPyVersions:
+    def __init__(self) -> None:
+        response = requests.get("https://api.github.com/repos/oracle/graalpython/releases")
+        response.raise_for_status()
+
+        releases = response.json()
+        gp_version_re = re.compile(r"-(\d+\.\d+\.\d+)$")
+        cp_version_re = re.compile(r"Python (\d+\.\d+(?:\.\d+)?)")
+        for release in releases:
+            m = gp_version_re.search(release["tag_name"])
+            if m:
+                release["graalpy_version"] = Version(m.group(1))
+            m = cp_version_re.search(release["body"])
+            if m:
+                release["python_version"] = Version(m.group(1))
+
+        self.releases = [r for r in releases if "graalpy_version" in r and "python_version" in r]
+
+    def update_version(self, identifier: str, spec: Specifier) -> AnyConfig:
+        if "x86_64" in identifier or "amd64" in identifier:
+            arch = "x86_64"
+        elif "arm64" in identifier or "aarch64" in identifier:
+            arch = "aarch64"
+        else:
+            msg = f"{identifier} not supported yet on GraalPy"
+            raise RuntimeError(msg)
+
+        releases = [r for r in self.releases if spec.contains(r["python_version"])]
+        releases = sorted(releases, key=lambda r: r["graalpy_version"])
+
+        if not releases:
+            msg = f"GraalPy {arch} not found for {spec}!"
+            raise RuntimeError(msg)
+
+        release = releases[-1]
+        version = release["python_version"]
+        gpversion = release["graalpy_version"]
+
+        if "macosx" in identifier:
+            arch = "x86_64" if "x86_64" in identifier else "arm64"
+            config = ConfigApple
+            platform = "macos"
+        elif "win" in identifier:
+            arch = "aarch64" if "arm64" in identifier else "x86_64"
+            config = ConfigWinGP
+            platform = "windows"
+        else:
+            msg = "GraalPy provides downloads for macOS and Windows and is included for manylinux"
+            raise RuntimeError(msg)
+
+        arch = "amd64" if arch == "x86_64" else "aarch64"
+        ext = "zip" if "win" in identifier else "tar.gz"
+        (url,) = (
+            rf["browser_download_url"]
+            for rf in release["assets"]
+            if rf["name"].endswith(f"{platform}-{arch}.{ext}")
+            and rf["name"].startswith(f"graalpy-{gpversion.major}")
+        )
+
+        return config(
+            identifier=identifier,
+            version=f"{version.major}.{version.minor}",
+            url=url,
+        )
+
+
 class PyPyVersions:
     def __init__(self, arch_str: ArchStr):
         response = requests.get("https://downloads.python.org/pypy/versions.json")
@@ -294,6 +367,8 @@ def __init__(self) -> None:
 
         self.ios_cpython = CPythonIOSVersions()
 
+        self.graalpy = GraalPyVersions()
+
     def update_config(self, config: MutableMapping[str, str]) -> None:
         identifier = config["identifier"]
         version = Version(config["version"])
@@ -311,6 +386,8 @@ def update_config(self, config: MutableMapping[str, str]) -> None:
                     config_update = self.macos_pypy.update_version_macos(spec)
                 elif "macosx_arm64" in identifier:
                     config_update = self.macos_pypy_arm64.update_version_macos(spec)
+            elif identifier.startswith("gp"):
+                config_update = self.graalpy.update_version(identifier, spec)
         elif "t-win32" in identifier and identifier.startswith("cp"):
             config_update = self.windows_t_32.update_version_windows(spec)
         elif "win32" in identifier and identifier.startswith("cp"):
@@ -322,6 +399,8 @@ def update_config(self, config: MutableMapping[str, str]) -> None:
                 config_update = self.windows_64.update_version_windows(spec)
             elif identifier.startswith("pp"):
                 config_update = self.windows_pypy_64.update_version_windows(spec)
+            elif identifier.startswith("gp"):
+                config_update = self.graalpy.update_version(identifier, spec)
         elif "t-win_arm64" in identifier and identifier.startswith("cp"):
             config_update = self.windows_t_arm64.update_version_windows(spec)
         elif "win_arm64" in identifier and identifier.startswith("cp"):
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index 321352e8c..80c2fb02e 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -243,6 +243,8 @@ def build_description_from_identifier(identifier: str) -> str:
         build_description += "CPython"
     elif python_interpreter == "pp":
         build_description += "PyPy"
+    elif python_interpreter == "gp":
+        build_description += "GraalPy"
     else:
         msg = f"unknown python {python_interpreter!r}"
         raise Exception(msg)
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 4b4a064d8..420e16139 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -103,7 +103,7 @@ def get_python_configurations(
     # skip builds as required by BUILD/SKIP
     python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
 
-    # filter-out some cross-compilation configs with PyPy:
+    # filter-out some cross-compilation configs with PyPy and GraalPy:
     # can't build arm64 on x86_64
     # rosetta allows to build x86_64 on arm64
     if platform.machine() == "x86_64":
@@ -111,7 +111,7 @@ def get_python_configurations(
         python_configurations = [
             c
             for c in python_configurations
-            if not (c.identifier.startswith("pp") and c.identifier.endswith("arm64"))
+            if not (c.identifier.startswith(("pp", "gp")) and c.identifier.endswith("arm64"))
         ]
         removed_elements = python_configurations_before - set(python_configurations)
         if removed_elements:
@@ -191,6 +191,22 @@ def install_pypy(tmp: Path, url: str) -> Path:
     return installation_path / "bin" / "pypy3"
 
 
+def install_graalpy(tmp: Path, url: str) -> Path:
+    graalpy_archive = url.rsplit("/", 1)[-1]
+    extension = ".tar.gz"
+    assert graalpy_archive.endswith(extension)
+    installation_path = CIBW_CACHE_PATH / graalpy_archive[: -len(extension)]
+    with FileLock(str(installation_path) + ".lock"):
+        if not installation_path.exists():
+            downloaded_archive = tmp / graalpy_archive
+            download(url, downloaded_archive)
+            installation_path.mkdir(parents=True)
+            # GraalPy top-folder name is inconsistent with archive name
+            call("tar", "-C", installation_path, "--strip-components=1", "-xzf", downloaded_archive)
+            downloaded_archive.unlink()
+    return installation_path / "bin" / "graalpy"
+
+
 def setup_python(
     tmp: Path,
     python_configuration: PythonConfiguration,
@@ -212,6 +228,8 @@ def setup_python(
 
     elif implementation_id.startswith("pp"):
         base_python = install_pypy(tmp, python_configuration.url)
+    elif implementation_id.startswith("gp"):
+        base_python = install_graalpy(tmp, python_configuration.url)
     else:
         msg = "Unknown Python implementation"
         raise ValueError(msg)
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index a63890644..91e72404b 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -123,6 +123,20 @@ def install_pypy(tmp: Path, arch: str, url: str) -> Path:
     return installation_path / "python.exe"
 
 
+def install_graalpy(tmp: Path, url: str) -> Path:
+    zip_filename = url.rsplit("/", 1)[-1]
+    extension = ".zip"
+    assert zip_filename.endswith(extension)
+    installation_path = CIBW_CACHE_PATH / zip_filename[: -len(extension)]
+    with FileLock(str(installation_path) + ".lock"):
+        if not installation_path.exists():
+            graalpy_zip = tmp / zip_filename
+            download(url, graalpy_zip)
+            # Extract to the parent directory because the zip file still contains a directory
+            extract_zip(graalpy_zip, installation_path.parent)
+    return installation_path / "bin" / "graalpy.exe"
+
+
 def setup_setuptools_cross_compile(
     tmp: Path,
     python_configuration: PythonConfiguration,
@@ -239,6 +253,8 @@ def setup_python(
     elif implementation_id.startswith("pp"):
         assert python_configuration.url is not None
         base_python = install_pypy(tmp, python_configuration.arch, python_configuration.url)
+    elif implementation_id.startswith("gp"):
+        base_python = install_graalpy(tmp, python_configuration.url or "")
     else:
         msg = "Unknown Python implementation"
         raise ValueError(msg)
@@ -314,6 +330,49 @@ def setup_python(
         setup_setuptools_cross_compile(tmp, python_configuration, python_libs_base, env)
         setup_rust_cross_compile(tmp, python_configuration, python_libs_base, env)
 
+    if implementation_id.startswith("gp"):
+        # GraalPy fails to discover compilers, setup the relevant environment
+        # variables. Adapted from
+        # https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt
+        # Remove when https://github.com/oracle/graalpython/issues/492 is fixed.
+        vcpath = subprocess.check_output(
+            [
+                Path(os.environ["PROGRAMFILES(X86)"])
+                / "Microsoft Visual Studio"
+                / "Installer"
+                / "vswhere.exe",
+                "-products",
+                "*",
+                "-latest",
+                "-property",
+                "installationPath",
+            ],
+            text=True,
+        ).strip()
+        log.notice(f"Discovering Visual Studio for GraalPy at {vcpath}")
+        env.update(
+            dict(
+                [
+                    envvar.strip().split("=", 1)
+                    for envvar in subprocess.check_output(
+                        [
+                            f"{vcpath}\\Common7\\Tools\\vsdevcmd.bat",
+                            "-no_logo",
+                            "-arch=amd64",
+                            "-host_arch=amd64",
+                            "&&",
+                            "set",
+                        ],
+                        shell=True,
+                        text=True,
+                        env=env,
+                    )
+                    .strip()
+                    .split("\n")
+                ]
+            )
+        )
+
     return base_python, env
 
 
@@ -342,6 +401,7 @@ def build(options: Options, tmp_path: Path) -> None:
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
             build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
+
             use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
             log.build_start(config.identifier)
 
@@ -390,6 +450,22 @@ def build(options: Options, tmp_path: Path) -> None:
                     build_frontend, build_options.build_verbosity, build_options.config_settings
                 )
 
+                if (
+                    config.identifier.startswith("gp")
+                    and build_frontend.name == "build"
+                    and "--no-isolation" not in extra_flags
+                    and "-n" not in extra_flags
+                ):
+                    # GraalPy fails to discover its standard library when a venv is created
+                    # from a virtualenv seeded executable. See
+                    # https://github.com/oracle/graalpython/issues/491 and remove this once
+                    # fixed upstream.
+                    log.notice(
+                        "Disabling build isolation to workaround GraalPy bug. If the build fails, consider using pip or build[uv] as build frontend."
+                    )
+                    shell("graalpy -m pip install setuptools wheel", env=env)
+                    extra_flags = [*extra_flags, "-n"]
+
                 build_env = env.copy()
                 if pip_version is not None:
                     build_env["VIRTUALENV_PIP"] = pip_version
@@ -414,6 +490,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
                     if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
                         extra_flags.append("--installer=uv")
+
                     call(
                         "python",
                         "-m",
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index d2ad15e09..d77ac1e2d 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -18,6 +18,7 @@ python_configurations = [
   { identifier = "pp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
   { identifier = "pp311-manylinux_x86_64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
+  { identifier = "gp242-manylinux_x86_64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
   { identifier = "cp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -57,6 +58,7 @@ python_configurations = [
   { identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
   { identifier = "pp311-manylinux_aarch64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
+  { identifier = "gp242-manylinux_aarch64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
   { identifier = "pp38-manylinux_i686", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
   { identifier = "pp39-manylinux_i686", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_i686", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
@@ -143,6 +145,8 @@ python_configurations = [
   { identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
   { identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_x86_64.tar.bz2" },
   { identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_arm64.tar.bz2" },
+  { identifier = "gp242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.0/graalpy-24.2.0-macos-amd64.tar.gz" },
+  { identifier = "gp242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.0/graalpy-24.2.0-macos-aarch64.tar.gz" },
 ]
 
 [windows]
@@ -171,6 +175,7 @@ python_configurations = [
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
   { identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-win64.zip" },
+  { identifier = "gp242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.0/graalpy-24.2.0-windows-amd64.zip" },
 ]
 
 [pyodide]
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 98c7a6601..48578712d 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -33,6 +33,7 @@ class EnableGroup(StrEnum):
     CPythonPrerelease = "cpython-prerelease"
     PyPy = "pypy"
     CPythonExperimentalRiscV64 = "cpython-experimental-riscv64"
+    GraalPy = "graalpy"
 
     @classmethod
     def all_groups(cls) -> frozenset["EnableGroup"]:
@@ -75,6 +76,8 @@ def __call__(self, build_id: str) -> bool:
             build_id, "*_riscv64"
         ):
             return False
+        if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"):
+            return False
 
         should_build = selector_matches(self.build_config, build_id)
         should_skip = selector_matches(self.skip_config, build_id)
diff --git a/docs/options.md b/docs/options.md
index 9d906b0c9..5006d8e28 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -323,6 +323,7 @@ values are:
 - `cpython-experimental-riscv64`: Enable experimental riscv64 builds. Those builds
   are disabled by default as they can't be uploaded to PyPI and a PEP will most likely
   be required before this can happen.
+- `graalpy`: Enable GraalPy.
 
 
 !!! caution
diff --git a/test/conftest.py b/test/conftest.py
index 8c0c9504c..f2b575df0 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -56,9 +56,8 @@ def docker_warmup(request: pytest.FixtureRequest) -> None:
     images = [build_options.manylinux_images[arch] for arch in archs] + [
         build_options.musllinux_images[arch] for arch in archs
     ]
-    # exclude GraalPy as it's not a target for cibuildwheel
     command = (
-        "manylinux-interpreters ensure $(manylinux-interpreters list 2>/dev/null | grep -v graalpy) &&"
+        "manylinux-interpreters ensure-all &&"
         "cpython3.13 -m pip download -d /tmp setuptools wheel pytest"
     )
     for image in images:
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
index ec529849d..5c7c27f54 100644
--- a/test/test_abi_variants.py
+++ b/test/test_abi_variants.py
@@ -41,9 +41,9 @@ def test_abi3(tmp_path):
     actual_wheels = utils.cibuildwheel_run(
         project_dir,
         add_env={
-            # free_threaded and PyPy do not have a Py_LIMITED_API equivalent, just build one of those
+            # free_threaded, GraalPy, and PyPy do not have a Py_LIMITED_API equivalent, just build one of those
             # also limit the number of builds for test performance reasons
-            "CIBW_BUILD": f"cp39-* cp310-* pp310-* {single_python_tag}-* cp313t-*"
+            "CIBW_BUILD": f"cp39-* cp310-* pp310-* gp242-* {single_python_tag}-* cp313t-*"
         },
     )
 
@@ -59,7 +59,11 @@ def test_abi3(tmp_path):
         expected_wheels = [
             w.replace("cp310-cp310", "cp310-abi3")
             for w in expected_wheels
-            if "-cp39" in w or "-cp310" in w or "-pp310" in w or "-cp313t" in w
+            if "-cp39" in w
+            or "-cp310" in w
+            or "-pp310" in w
+            or "-graalpy242" in w
+            or "-cp313t" in w
         ]
     assert set(actual_wheels) == set(expected_wheels)
 
diff --git a/test/test_before_all.py b/test/test_before_all.py
index a46031ee7..c7ec86240 100644
--- a/test/test_before_all.py
+++ b/test/test_before_all.py
@@ -38,8 +38,8 @@ def test(tmp_path):
 
     # build the wheels
     before_all_command = (
-        """python -c "import os, sys;open('{project}/text_info.txt', 'w').write('sample text '+os.environ.get('TEST_VAL', ''))" && """
-        '''python -c "import sys; open('{project}/python_prefix.txt', 'w').write(sys.prefix)"'''
+        """python -c "import os, pathlib, sys; pathlib.Path('{project}/text_info.txt').write_text('sample text '+os.environ.get('TEST_VAL', ''))" && """
+        '''python -c "import pathlib, sys; pathlib.Path('{project}/python_prefix.txt').write_text(sys.prefix)"'''
     )
     actual_wheels = utils.cibuildwheel_run(
         project_dir,
diff --git a/test/test_before_build.py b/test/test_before_build.py
index 19a770133..9494de7bd 100644
--- a/test/test_before_build.py
+++ b/test/test_before_build.py
@@ -41,8 +41,8 @@ def test(tmp_path):
     project_with_before_build_asserts.generate(project_dir)
 
     before_build = (
-        """python -c "import sys; open('{project}/pythonversion_bb.txt', 'w').write(sys.version)" && """
-        f'''python -c "import sys; open('{{project}}/pythonprefix_bb.txt', 'w').write({SYS_PREFIX})"'''
+        """python -c "import pathlib, sys; pathlib.Path('{project}/pythonversion_bb.txt').write_text(sys.version)" && """
+        f'''python -c "import pathlib, sys; pathlib.Path('{{project}}/pythonprefix_bb.txt').write_text({SYS_PREFIX})"'''
     )
     frontend = "build"
     if utils.platform != "pyodide":
diff --git a/test/test_before_test.py b/test/test_before_test.py
index 7e766abaa..a3700f843 100644
--- a/test/test_before_test.py
+++ b/test/test_before_test.py
@@ -7,7 +7,7 @@
 from pathlib import Path
 from unittest import TestCase
 
-PROJECT_DIR = Path(__file__).joinpath("..", "..").resolve()
+PROJECT_DIR = Path(__file__).parent.parent.resolve()
 
 
 class TestBeforeTest(TestCase):
@@ -39,8 +39,8 @@ def test(tmp_path, build_frontend_env):
     test_projects.new_c_project().generate(test_project_dir)
 
     before_test_steps = [
-        '''python -c "import os, sys; open('{project}/pythonversion_bt.txt', 'w').write(sys.version)"''',
-        '''python -c "import os, sys; open('{project}/pythonprefix_bt.txt', 'w').write(sys.prefix)"''',
+        '''python -c "import pathlib, sys; pathlib.Path('{project}/pythonversion_bt.txt').write_text(sys.version)"''',
+        '''python -c "import pathlib, sys; pathlib.Path('{project}/pythonprefix_bt.txt').write_text(sys.prefix)"''',
     ]
 
     if utils.platform == "pyodide":
@@ -63,7 +63,9 @@ def test(tmp_path, build_frontend_env):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
-            "CIBW_TEST_COMMAND_WINDOWS": "pytest ./test",
+            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
+            # until https://github.com/oracle/graalpython/issues/490 is fixed
+            "CIBW_TEST_COMMAND_WINDOWS": "where graalpy || pytest ./test",
             **build_frontend_env,
         },
     )
diff --git a/test/test_dependency_versions.py b/test/test_dependency_versions.py
index b8d80f3bc..d4b9416f3 100644
--- a/test/test_dependency_versions.py
+++ b/test/test_dependency_versions.py
@@ -128,6 +128,17 @@ def test_dependency_constraints(method, tmp_path, build_frontend_env_nouv):
 
     build_environment = {}
 
+    if (
+        utils.platform == "windows"
+        and method == "file"
+        and build_frontend_env_nouv["CIBW_BUILD_FRONTEND"] == "build"
+    ):
+        # GraalPy fails to discover its standard library when a venv is created
+        # from a virtualenv seeded executable. See
+        # https://github.com/oracle/graalpython/issues/491 and remove this once
+        # fixed upstream.
+        build_frontend_env_nouv["CIBW_SKIP"] = "gp*"
+
     for package_name, version in tool_versions.items():
         env_name = f"EXPECTED_{package_name.upper()}_VERSION"
         build_environment[env_name] = version
@@ -147,4 +158,13 @@ def test_dependency_constraints(method, tmp_path, build_frontend_env_nouv):
     # also check that we got the right wheels
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
 
+    if (
+        utils.platform == "windows"
+        and method == "file"
+        and build_frontend_env_nouv["CIBW_BUILD_FRONTEND"] == "build"
+    ):
+        # See reference to https://github.com/oracle/graalpython/issues/491
+        # above
+        expected_wheels = [w for w in expected_wheels if "graalpy" not in w]
+
     assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/test_pep518.py b/test/test_pep518.py
index 2147520f9..c861f0da3 100644
--- a/test/test_pep518.py
+++ b/test/test_pep518.py
@@ -33,11 +33,26 @@ def test_pep518(tmp_path, build_frontend_env):
     project_dir = tmp_path / "project"
     basic_project.generate(project_dir)
 
+    # GraalPy fails to discover its standard library when a venv is created
+    # from a virtualenv seeded executable. See
+    # https://github.com/oracle/graalpython/issues/491 and remove this once
+    # fixed upstream.
+    if build_frontend_env["CIBW_BUILD_FRONTEND"] == "build" and utils.platform == "windows":
+        build_frontend_env["CIBW_SKIP"] = "gp*"
+
     # build the wheels
     actual_wheels = utils.cibuildwheel_run(project_dir, add_env=build_frontend_env)
 
     # check that the expected wheels are produced
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
+
+    # GraalPy fails to discover its standard library when a venv is created
+    # from a virtualenv seeded executable. See
+    # https://github.com/oracle/graalpython/issues/491 and remove this once
+    # fixed upstream.
+    if build_frontend_env["CIBW_BUILD_FRONTEND"] == "build" and utils.platform == "windows":
+        expected_wheels = [w for w in expected_wheels if "graalpy" not in w]
+
     assert set(actual_wheels) == set(expected_wheels)
 
     # These checks ensure an extra file is not created when using custom
diff --git a/test/test_testing.py b/test/test_testing.py
index 16f00aa60..9b7364f99 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -81,7 +81,9 @@ def test(tmp_path):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest ./test",
+            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
+            # until https://github.com/oracle/graalpython/issues/490 is fixed
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest ./test",
         },
     )
 
@@ -102,7 +104,9 @@ def test_extras_require(tmp_path):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest ./test",
+            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
+            # until https://github.com/oracle/graalpython/issues/490 is fixed
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest ./test",
         },
         single_python=True,
     )
@@ -134,7 +138,9 @@ def test_dependency_groups(tmp_path):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest ./test",
+            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
+            # until https://github.com/oracle/graalpython/issues/490 is fixed
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest ./test",
         },
         single_python=True,
     )
@@ -189,7 +195,9 @@ def test_bare_pytest_invocation(tmp_path: Path, test_runner: str) -> None:
         add_env={
             "CIBW_TEST_REQUIRES": "pytest" if test_runner == "pytest" else "",
             "CIBW_TEST_COMMAND": (
-                "python -m pytest"
+                # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
+                # until https://github.com/oracle/graalpython/issues/490 fixed
+                "graalpy.exe -c 1 || python -m pytest"
                 if test_runner == "pytest"
                 else "python -m unittest discover test spam_test.py"
             ),
@@ -211,7 +219,9 @@ def test_test_sources(tmp_path):
         add_env={
             "CIBW_TEST_REQUIRES": "pytest",
             "CIBW_TEST_COMMAND": "pytest",
-            "CIBW_TEST_COMMAND_WINDOWS": "pytest",
+            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
+            # until https://github.com/oracle/graalpython/issues/490 is fixed
+            "CIBW_TEST_COMMAND_WINDOWS": "where graalpy || pytest",
             "CIBW_TEST_SOURCES": "test",
         },
     )
diff --git a/test/utils.py b/test/utils.py
index e05d3a2dd..957ee7295 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -55,7 +55,7 @@ def cibuildwheel_get_build_identifiers(
     cmd = [sys.executable, "-m", "cibuildwheel", "--print-build-identifiers", str(project_path)]
     if env is None:
         env = os.environ.copy()
-    env["CIBW_ENABLE"] = "cpython-freethreading pypy"
+    env["CIBW_ENABLE"] = "cpython-freethreading pypy graalpy"
     if prerelease_pythons:
         env["CIBW_ENABLE"] += " cpython-prerelease"
 
@@ -257,6 +257,10 @@ def _expected_wheels(
                 "pp310-pypy310_pp73",
                 "pp311-pypy311_pp73",
             ]
+        if machine_arch in ["x86_64", "AMD64", "aarch64", "arm64"]:
+            python_abi_tags += [
+                "graalpy311-graalpy242_311_native",
+            ]
 
     if single_python:
         python_tag = "cp{}{}-".format(*SINGLE_PYTHON_VERSION)
@@ -286,7 +290,7 @@ def _expected_wheels(
                         for manylinux_version in manylinux_versions
                     )
                 ]
-            if len(musllinux_versions) > 0 and not python_abi_tag.startswith("pp"):
+            if len(musllinux_versions) > 0 and not python_abi_tag.startswith(("pp", "graalpy")):
                 platform_tags.append(
                     ".".join(
                         f"{musllinux_version}_{machine_arch}"
diff --git a/unit_test/linux_build_steps_test.py b/unit_test/linux_build_steps_test.py
index cd295a5e9..aa4363c13 100644
--- a/unit_test/linux_build_steps_test.py
+++ b/unit_test/linux_build_steps_test.py
@@ -24,7 +24,7 @@ def test_linux_container_split(tmp_path: Path, monkeypatch: pytest.MonkeyPatch)
                 manylinux-x86_64-image = "normal_container_image"
                 manylinux-i686-image = "normal_container_image"
                 build = "*-manylinux_x86_64"
-                skip = "pp*"
+                skip = "[gp]p*"
                 archs = "x86_64 i686"
 
                 [[tool.cibuildwheel.overrides]]
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index 48723213b..66ee4c952 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -423,9 +423,9 @@ def test_debug_traceback(monkeypatch, method, capfd):
 @pytest.mark.parametrize("method", ["unset", "command_line", "env_var"])
 def test_enable(method, intercepted_build_args, monkeypatch):
     if method == "command_line":
-        monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "pypy"])
+        monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "pypy", "--enable", "graalpy"])
     elif method == "env_var":
-        monkeypatch.setenv("CIBW_ENABLE", "pypy")
+        monkeypatch.setenv("CIBW_ENABLE", "pypy graalpy")
 
     main()
 
@@ -434,18 +434,20 @@ def test_enable(method, intercepted_build_args, monkeypatch):
     if method == "unset":
         assert enable_groups == frozenset()
     else:
-        assert enable_groups == frozenset([EnableGroup.PyPy])
+        assert enable_groups == frozenset([EnableGroup.PyPy, EnableGroup.GraalPy])
 
 
 def test_enable_arg_inherits(intercepted_build_args, monkeypatch):
-    monkeypatch.setenv("CIBW_ENABLE", "pypy")
+    monkeypatch.setenv("CIBW_ENABLE", "pypy graalpy")
     monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "cpython-prerelease"])
 
     main()
 
     enable_groups = intercepted_build_args.args[0].globals.build_selector.enable
 
-    assert enable_groups == frozenset((EnableGroup.PyPy, EnableGroup.CPythonPrerelease))
+    assert enable_groups == frozenset(
+        (EnableGroup.PyPy, EnableGroup.GraalPy, EnableGroup.CPythonPrerelease)
+    )
 
 
 def test_enable_arg_error_message(monkeypatch, capsys):
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 4a24190c1..d5dc7d191 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -14,7 +14,7 @@
 from cibuildwheel.util import file
 
 DEFAULT_IDS = {"cp38", "cp39", "cp310", "cp311", "cp312", "cp313"}
-ALL_IDS = DEFAULT_IDS | {"cp313t", "pp38", "pp39", "pp310", "pp311"}
+ALL_IDS = DEFAULT_IDS | {"cp313t", "pp38", "pp39", "pp310", "pp311", "gp242"}
 
 
 @pytest.fixture
@@ -103,7 +103,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 [tool.cibuildwheel]
 manylinux-x86_64-image = "manylinux_2_28"
 musllinux-x86_64-image = "musllinux_1_2"
-enable = ["pypy", "cpython-freethreading"]
+enable = ["pypy", "graalpy", "cpython-freethreading"]
 
 # Before Python 3.10, use manylinux2014
 [[tool.cibuildwheel.overrides]]
@@ -155,6 +155,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
             "pp39",
             "pp310",
             "pp311",
+            "gp242",
         }
     }
     assert kwargs["options"].build_options("cp39-manylinux_x86_64").before_all == ""
@@ -176,6 +177,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
             "pp39",
             "pp310",
             "pp311",
+            "gp242",
         ]
     }
 
@@ -185,14 +187,16 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
     assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
 
     identifiers = {x.identifier for x in kwargs["platform_configs"]}
-    assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS}
+    assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS if "gp" not in x}
 
     kwargs = build_in_container.call_args_list[4][1]
     assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
     identifiers = {x.identifier for x in kwargs["platform_configs"]}
-    assert identifiers == {f"{x}-musllinux_x86_64" for x in ALL_IDS if "pp" not in x}
+    assert identifiers == {
+        f"{x}-musllinux_x86_64" for x in ALL_IDS if "pp" not in x and "gp" not in x
+    }
 
     kwargs = build_in_container.call_args_list[5][1]
     assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"]
@@ -200,4 +204,6 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
     assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
 
     identifiers = {x.identifier for x in kwargs["platform_configs"]}
-    assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x}
+    assert identifiers == {
+        f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x and "gp" not in x
+    }

From e91560e8a14470ff35488e414aa7aa1de5aec2d8 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 18:25:42 -0400
Subject: [PATCH 055/193] [pre-commit.ci] pre-commit autoupdate (#2376)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.6 → v0.11.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.6...v0.11.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index fb55aa5bf..55d0b9df9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.6
+  rev: v0.11.7
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 048ff03577c40eec979f98f0db8452c3d5749bed Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 May 2025 16:21:01 +0100
Subject: [PATCH 056/193] chore(deps): bump the actions group with 2 updates
 (#2379)

Bumps the actions group with 2 updates: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) and [wntrblm/nox](https://github.com/wntrblm/nox).


Updates `actions/attest-build-provenance` from 2.2.3 to 2.3.0
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/c074443f1aee8d4aeeae555aebba3282517141b2...db473fddc028af60658334401dc6fa3ffd8669fd)

Updates `wntrblm/nox` from 2025.02.09 to 2025.05.01
- [Release notes](https://github.com/wntrblm/nox/releases)
- [Changelog](https://github.com/wntrblm/nox/blob/main/CHANGELOG.md)
- [Commits](https://github.com/wntrblm/nox/compare/2025.02.09...2025.05.01)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: wntrblm/nox
  dependency-version: 2025.05.01
  dependency-type: direct:production
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/release.yml             | 2 +-
 .github/workflows/update-dependencies.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d9d48888b..34e3aacea 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,7 +33,7 @@ jobs:
         path: dist
 
     - name: Generate artifact attestation for sdist and wheel
-      uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
+      uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0
       with:
         subject-path: "dist/cibuildwheel-*"
 
diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml
index a31aaf887..c22469df6 100644
--- a/.github/workflows/update-dependencies.yml
+++ b/.github/workflows/update-dependencies.yml
@@ -32,7 +32,7 @@ jobs:
 
     - uses: actions/checkout@v4
 
-    - uses: wntrblm/nox@2025.02.09
+    - uses: wntrblm/nox@2025.05.01
 
     - name: "Run update: dependencies"
       run: nox --force-color -s update_constraints

From 143ec07556449851fc06e0faa680af9cd9be365e Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 5 May 2025 16:23:25 -0400
Subject: [PATCH 057/193] [Bot] Update dependencies (#2374)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   |  6 +--
 .../resources/constraints-pyodide312.txt      | 16 +++---
 .../resources/constraints-python310.txt       |  4 +-
 .../resources/constraints-python311.txt       |  2 +-
 .../resources/constraints-python312.txt       |  2 +-
 .../resources/constraints-python313.txt       |  2 +-
 .../resources/constraints-python39.txt        |  4 +-
 cibuildwheel/resources/constraints.txt        |  2 +-
 cibuildwheel/resources/nodejs.toml            |  4 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 docs/working-examples.md                      | 16 +++---
 11 files changed, 56 insertions(+), 56 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index d77ac1e2d..15f534022 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -145,8 +145,8 @@ python_configurations = [
   { identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
   { identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_x86_64.tar.bz2" },
   { identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_arm64.tar.bz2" },
-  { identifier = "gp242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.0/graalpy-24.2.0-macos-amd64.tar.gz" },
-  { identifier = "gp242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.0/graalpy-24.2.0-macos-aarch64.tar.gz" },
+  { identifier = "gp242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-amd64.tar.gz" },
+  { identifier = "gp242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-aarch64.tar.gz" },
 ]
 
 [windows]
@@ -175,7 +175,7 @@ python_configurations = [
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
   { identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-win64.zip" },
-  { identifier = "gp242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.0/graalpy-24.2.0-windows-amd64.zip" },
+  { identifier = "gp242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-windows-amd64.zip" },
 ]
 
 [pyodide]
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index e7bd14b30..2580c967b 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -10,12 +10,12 @@ build==1.2.2.post1
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   pyodide-build
-certifi==2025.1.31
+certifi==2025.4.26
     # via
     #   httpcore
     #   httpx
     #   requests
-charset-normalizer==3.4.1
+charset-normalizer==3.4.2
     # via requests
 click==8.1.8
     # via typer
@@ -25,9 +25,9 @@ distlib==0.3.9
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
-h11==0.14.0
+h11==0.16.0
     # via httpcore
-httpcore==1.0.8
+httpcore==1.0.9
     # via httpx
 httpx==0.28.1
     # via unearth
@@ -48,15 +48,15 @@ packaging==25.0
     #   build
     #   pyodide-build
     #   unearth
-pip==25.0.1
+pip==25.1.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
 platformdirs==4.3.7
     # via virtualenv
-pydantic==2.11.3
+pydantic==2.11.4
     # via
     #   pyodide-build
     #   pyodide-lock
-pydantic-core==2.33.1
+pydantic-core==2.33.2
     # via pydantic
 pygments==2.19.1
     # via rich
@@ -87,7 +87,7 @@ shellingham==1.5.4
     # via typer
 sniffio==1.3.1
     # via anyio
-typer==0.15.2
+typer==0.15.3
     # via
     #   auditwheel-emscripten
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 9eccc27be..3f3ca6dca 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -10,7 +10,7 @@ distlib==0.3.9
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
-importlib-metadata==8.6.1
+importlib-metadata==8.7.0
     # via build
 macholib==1.16.3
     # via delocate
@@ -18,7 +18,7 @@ packaging==25.0
     # via
     #   build
     #   delocate
-pip==25.0.1
+pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
 platformdirs==4.3.7
     # via virtualenv
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 7abb00bfa..b3f0cf64c 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -16,7 +16,7 @@ packaging==25.0
     # via
     #   build
     #   delocate
-pip==25.0.1
+pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
 platformdirs==4.3.7
     # via virtualenv
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 7abb00bfa..b3f0cf64c 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -16,7 +16,7 @@ packaging==25.0
     # via
     #   build
     #   delocate
-pip==25.0.1
+pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
 platformdirs==4.3.7
     # via virtualenv
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 7abb00bfa..b3f0cf64c 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -16,7 +16,7 @@ packaging==25.0
     # via
     #   build
     #   delocate
-pip==25.0.1
+pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
 platformdirs==4.3.7
     # via virtualenv
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 9eccc27be..3f3ca6dca 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -10,7 +10,7 @@ distlib==0.3.9
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
-importlib-metadata==8.6.1
+importlib-metadata==8.7.0
     # via build
 macholib==1.16.3
     # via delocate
@@ -18,7 +18,7 @@ packaging==25.0
     # via
     #   build
     #   delocate
-pip==25.0.1
+pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
 platformdirs==4.3.7
     # via virtualenv
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 7abb00bfa..b3f0cf64c 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -16,7 +16,7 @@ packaging==25.0
     # via
     #   build
     #   delocate
-pip==25.0.1
+pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
 platformdirs==4.3.7
     # via virtualenv
diff --git a/cibuildwheel/resources/nodejs.toml b/cibuildwheel/resources/nodejs.toml
index ad93f2d7b..9674bc39f 100644
--- a/cibuildwheel/resources/nodejs.toml
+++ b/cibuildwheel/resources/nodejs.toml
@@ -1,3 +1,3 @@
 url = "https://nodejs.org/dist/"
-v22 = "v22.14.0"
-v20 = "v20.19.0"
+v22 = "v22.15.0"
+v20 = "v20.19.1"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 7a9962a39..3d1b3283c 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.03-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.03-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.26-0
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.03-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.03-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.03-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.03-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.03-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.26-0
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.03-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.03-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.03-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.26-0
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.03-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.03-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.03-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.26-0
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.03-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.03-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.03-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.26-0
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.03-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.03-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.03-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.26-0
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.19-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.03-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.03-1
 
 [riscv64]
 
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 63574dd2d..366d6f257 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -51,8 +51,8 @@ title: Working examples
 | [PyYAML][]                        | ![github icon][] | ![apple icon][] | Canonical source repository for PyYAML |
 | [pikepdf][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python library for reading and writing PDF, powered by QPDF |
 | [numexpr][]                       | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast numerical array expression evaluator for Python, NumPy, Pandas, PyTables and more |
-| [h5py][]                          | ![azurepipelines icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | HDF5 for Python -- The h5py package is a Pythonic interface to the HDF5 binary data format. |
 | [Wrapt][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python module for decorators, wrappers and monkey patching. |
+| [h5py][]                          | ![azurepipelines icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | HDF5 for Python -- The h5py package is a Pythonic interface to the HDF5 binary data format. |
 | [envd][]                          | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A machine learning development environment build tool |
 | [Psycopg 3][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A modern implementation of a PostgreSQL adapter for Python |
 | [OpenColorIO][]                   | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. |
@@ -98,13 +98,13 @@ title: Working examples
 | [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
 | [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
-| [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
 | [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
+| [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
 | [Arbor][]                         | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries.  |
-| [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
 | [clang-format][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. |
-| [etebase-py][]                    | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. |
+| [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
 | [cf-units][]                      | ![github icon][] | ![apple icon][] ![linux icon][] | Units of measure as required by the Climate and Forecast (CF) Metadata Conventions |
+| [etebase-py][]                    | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. |
 | [ninja][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
 | [numpythia][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
 | [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
@@ -161,8 +161,8 @@ title: Working examples
 [PyYAML]: https://github.com/yaml/pyyaml
 [pikepdf]: https://github.com/pikepdf/pikepdf
 [numexpr]: https://github.com/pydata/numexpr
-[h5py]: https://github.com/h5py/h5py
 [Wrapt]: https://github.com/GrahamDumpleton/wrapt
+[h5py]: https://github.com/h5py/h5py
 [envd]: https://github.com/tensorchord/envd
 [Psycopg 3]: https://github.com/psycopg/psycopg
 [OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO
@@ -208,13 +208,13 @@ title: Working examples
 [boost-histogram]: https://github.com/scikit-hep/boost-histogram
 [Python-WebRTC]: https://github.com/MarshalX/python-webrtc
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
-[fathon]: https://github.com/stfbnc/fathon
 [pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
+[fathon]: https://github.com/stfbnc/fathon
 [Arbor]: https://github.com/arbor-sim/arbor
-[polaroid]: https://github.com/daggy1234/polaroid
 [clang-format]: https://github.com/ssciwr/clang-format-wheel
-[etebase-py]: https://github.com/etesync/etebase-py
+[polaroid]: https://github.com/daggy1234/polaroid
 [cf-units]: https://github.com/SciTools/cf-units
+[etebase-py]: https://github.com/etesync/etebase-py
 [ninja]: https://github.com/scikit-build/ninja-python-distributions
 [numpythia]: https://github.com/scikit-hep/numpythia
 [pyjet]: https://github.com/scikit-hep/pyjet

From edbd234b820f271eca6dadad30c6a2d67b5cb685 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 5 May 2025 16:23:35 -0400
Subject: [PATCH 058/193] [pre-commit.ci] pre-commit autoupdate (#2381)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.7 → v0.11.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.7...v0.11.8)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 55d0b9df9..41be3c2a2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.7
+  rev: v0.11.8
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 04c9427a2015a0f076d0cef047ffd2c2c3af5c1f Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Thu, 8 May 2025 12:27:05 +0100
Subject: [PATCH 059/193] Test enable groups as specified by PR labels (#2357)

* Allow CIBW_ENABLE to control the wheels built in testing

* Set CIBW_ENABLE using PR labels

* Add docs

* Build everything on the main branch

* Add CIBW_ENABLE=all option

This was mostly for use in the `main` building case, because otherwise it's maybe a bit too easy to forget to update this file when adding an enable group

* Remove dead code

* Make unit tests robust to the value of CIBW_ENABLE

* Fix tests that explicitly choose pypy

* Fix test expectation

* Don't expect impossible wheels in expected_wheels

* Simplify logic in expected_wheels

* CircleCI- run with CIBW_ENABLE=all only on the main branch

* Azure pipelines - run with CIBW_ENABLE=all on main branch

* Update gitlab to run CIBW_ENABLE=all on main

* Set CIBW_ENABLE=all on travis - it only runs on main anyway

* Fix job name error on CircleCI

* Fix tests for graalpy

* Update the test configuration to use the label

* Remove duplication of default value. Make it affect sample build too

* Move the action to after deps are installed

* GraalPy workaround for this assumption

* Make unit test resilient to changing CIBW_ENABLE
---
 .circleci/config.yml                      | 63 ++++++++++++-----------
 .github/workflows/test.yml                | 26 +++++++++-
 .gitlab-ci.yml                            | 12 +++++
 .travis.yml                               | 14 +++--
 azure-pipelines.yml                       | 18 +++++++
 cibuildwheel/options.py                   |  6 ++-
 cibuildwheel/selector.py                  | 17 ++++++
 docs/contributing.md                      |  2 +
 docs/options.md                           |  2 +-
 test/conftest.py                          |  6 +++
 test/test_0_basic.py                      | 25 +++++----
 test/test_abi_variants.py                 | 35 ++++++-------
 test/utils.py                             | 47 ++++++++++-------
 unit_test/main_tests/main_options_test.py | 12 +++++
 unit_test/option_prepare_test.py          |  1 +
 15 files changed, 204 insertions(+), 82 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5c8bef6f6..13bcbe069 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,7 +1,30 @@
-version: 2
+version: 2.1
+
+commands:
+  cibw_prepare_environment:
+    description: "Prepare the environment for testing."
+    steps:
+      - run:
+          name: Prepare the environment.
+          command: bash .circleci/prepare.sh
+  cibw_run_tests:
+    description: "Runs tests, with CIBW_ENABLE=all on the main branch"
+    steps:
+      - run:
+          name: Test
+          command: |
+            if [ "${CIRCLE_BRANCH}" == "main" ]; then
+              echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
+              export CIBW_ENABLE=all
+            else
+              echo "INFO: CIBW_ENABLE not set for this branch test run."
+            fi
+
+            venv/bin/python ./bin/run_tests.py
+          no_output_timeout: 30m
 
 jobs:
-  osx-python3.12:
+  osx-python312:
     macos:
       xcode: 15.4.0
     resource_class: macos.m1.medium.gen1
@@ -9,16 +32,10 @@ jobs:
       PYTHON: python3
     steps:
       - checkout
+      - cibw_prepare_environment
+      - cibw_run_tests
 
-      - run:
-          name: Prepare the environment.
-          command: bash .circleci/prepare.sh
-      - run:
-          name: Test.
-          command: venv/bin/python ./bin/run_tests.py
-          no_output_timeout: 30m
-
-  linux-python3.12:
+  linux-python312:
     docker:
       - image: cimg/python:3.12
     environment:
@@ -29,14 +46,8 @@ jobs:
     steps:
       - checkout
       - setup_remote_docker
-
-      - run:
-          name: Prepare the environment.
-          command: bash .circleci/prepare.sh
-      - run:
-          name: Test.
-          command: venv/bin/python ./bin/run_tests.py
-          no_output_timeout: 30m
+      - cibw_prepare_environment
+      - cibw_run_tests
 
   linux-aarch64:
     machine:
@@ -49,19 +60,13 @@ jobs:
       PYTEST_ADDOPTS: -k "unit_test or main_tests or test_0_basic or test_docker_images"
     steps:
       - checkout
-
-      - run:
-          name: Prepare the environment.
-          command: bash .circleci/prepare.sh
-      - run:
-          name: Test.
-          command: venv/bin/python ./bin/run_tests.py
-          no_output_timeout: 30m
+      - cibw_prepare_environment
+      - cibw_run_tests
 
 workflows:
   version: 2
   all-tests:
     jobs:
-      - osx-python3.12
-      - linux-python3.12
+      - osx-python312
+      - linux-python312
       - linux-aarch64
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 3ae094dcd..d860ef40b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,6 +6,11 @@ on:
       - main
       - 2.x
   pull_request:
+    types:
+      - opened
+      - synchronize
+      - reopened
+      - labeled
     paths-ignore:
       - 'docs/**'
       - .pre-commit-config.yaml
@@ -68,6 +73,26 @@ jobs:
       run: |
         uv sync --no-dev --group test
 
+    - uses: joerick/pr-labels-action@v1.0.9
+    - name: Set CIBW_ENABLE
+      shell: bash
+      run: |
+        if [[ "${{ github.ref_name }}" == "main" ]]; then
+          CIBW_ENABLE=all
+        else
+          # get the default CIBW_ENABLE value from the test module
+          CIBW_ENABLE=$(uv run --no-sync python -c 'import sys, test.conftest as c; sys.stdout.write(c.DEFAULT_CIBW_ENABLE)')
+
+          # if this is a PR, check for labels
+          if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then
+            CIBW_ENABLE+=" pypy"
+          fi
+          if [[ -n "$GITHUB_PR_LABEL_CI_GRAALPY" ]]; then
+            CIBW_ENABLE+=" graalpy"
+          fi
+        fi
+        echo "CIBW_ENABLE=${CIBW_ENABLE}" >> $GITHUB_ENV
+
     - name: Generate a sample project
       run: |
         uv run --no-sync -m test.test_projects test.test_0_basic.basic_project sample_proj
@@ -80,7 +105,6 @@ jobs:
       env:
         CIBW_ARCHS_MACOS: x86_64 universal2 arm64
         CIBW_BUILD_FRONTEND: 'build[uv]'
-        CIBW_ENABLE: "cpython-prerelease cpython-freethreading pypy graalpy"
 
     - name: Run a sample build (GitHub Action, only)
       uses: ./
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ecf0361fb..5ee98dd54 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -13,6 +13,10 @@ linux:
     # skip all but the basic tests
     # (comment the below line in a PR to debug a Gitlab-specific issue)
     PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "main"'
+      variables:
+        CIBW_ENABLE: "all"
   script:
     - curl -sSL https://get.docker.com/ | sh
     - docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
@@ -26,6 +30,10 @@ windows:
     PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
   before_script:
     - choco install python -y --version 3.12.4
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "main"'
+      variables:
+        CIBW_ENABLE: "all"
   script:
     - py -m pip install dependency-groups
     - py -m pip install -e. pytest-custom-exit-code $(py -m dependency_groups test)
@@ -37,6 +45,10 @@ macos:
   image: macos-14-xcode-15
   variables:
     PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "main"'
+      variables:
+        CIBW_ENABLE: "all"
   script:
     - python3 -m pip install dependency-groups
     - python3 -m dependency_groups test | xargs python3 -m pip install -e. pytest-custom-exit-code
diff --git a/.travis.yml b/.travis.yml
index ec2365a09..e4d1d72a3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,13 +14,17 @@ jobs:
     - name: Linux | x86_64 + i686 | Python 3.12
       python: 3.12
       services: docker
-      env: PYTHON=python
+      env:
+        - PYTHON=python
+        - CIBW_ENABLE=all
 
     - name: Linux | arm64 | Python 3.12
       python: 3.12
       services: docker
       arch: arm64
-      env: PYTHON=python
+      env:
+        - PYTHON=python
+        - CIBW_ENABLE=all
 
     - name: Linux | ppc64le | Python 3.12
       python: 3.12
@@ -32,6 +36,7 @@ jobs:
         # skip test_manylinuxXXXX_only, it uses too much disk space
         # c.f. https://travis-ci.community/t/running-out-of-disk-space-quota-when-using-docker-on-ppc64le/11634
         - PYTEST_ADDOPTS='-k "not test_manylinuxXXXX_only"'
+        - CIBW_ENABLE=all
 
     - name: Windows | x86_64 | Python 3.12
       os: windows
@@ -40,13 +45,16 @@ jobs:
         - choco upgrade python3 -y --version 3.12.8 --limit-output --params "/InstallDir:C:\\Python312"
       env:
         - PYTHON=C:\\Python312\\python
+        - CIBW_ENABLE=all
 
     - name: Linux | s390x | Python 3.12
       python: 3.12
       services: docker
       arch: s390x
       allow_failure: True
-      env: PYTHON=python
+      env:
+        - PYTHON=python
+        - CIBW_ENABLE=all
 
 install:
 - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all; fi
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 1cc707fd9..ddc9f18fa 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -16,6 +16,12 @@ jobs:
         docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
         python -m pip install dependency-groups
         python -m dependency_groups test | xargs python -m pip install -e.
+        if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
+          echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
+          export CIBW_ENABLE=all
+        else
+          echo "INFO: CIBW_ENABLE not set for this branch ($(Build.SourceBranch))."
+        fi
         python ./bin/run_tests.py
 
 - job: macos_311
@@ -28,6 +34,12 @@ jobs:
     - bash: |
         python -m pip install dependency-groups
         python -m dependency_groups test | xargs python -m pip install -e.
+        if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
+          echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
+          export CIBW_ENABLE=all
+        else
+          echo "INFO: CIBW_ENABLE not set for this branch ($(Build.SourceBranch))."
+        fi
         python ./bin/run_tests.py
 
 - job: windows_311
@@ -40,4 +52,10 @@ jobs:
     - bash: |
         python -m pip install dependency-groups
         python -m dependency_groups test | xargs python -m pip install -e.
+        if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
+          echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
+          export CIBW_ENABLE=all
+        else
+          echo "INFO: CIBW_ENABLE not set for this branch ($(Build.SourceBranch))."
+        fi
         python ./bin/run_tests.py
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 283f48f7c..fa1d1c046 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -634,8 +634,10 @@ def globals(self) -> GlobalOptions:
             "enable", env_plat=False, option_format=ListFormat(sep=" "), env_rule=InheritRule.APPEND
         )
         try:
-            enable = {EnableGroup(group) for group in enable_groups.split()}
-            enable.update(EnableGroup(command_line_group) for command_line_group in args.enable)
+            enable = {
+                *EnableGroup.parse_option_value(enable_groups),
+                *EnableGroup.parse_option_value(" ".join(args.enable)),
+            }
         except ValueError as e:
             msg = f"Failed to parse enable group. {e}. Valid group names are: {', '.join(g.value for g in EnableGroup)}"
             raise errors.ConfigurationError(msg) from e
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 48578712d..2d78aea0e 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -39,6 +39,23 @@ class EnableGroup(StrEnum):
     def all_groups(cls) -> frozenset["EnableGroup"]:
         return frozenset(cls)
 
+    @classmethod
+    def parse_option_value(cls, value: str) -> frozenset["EnableGroup"]:
+        """
+        Parses a string of space-separated values into a set of EnableGroup
+        members. The string may contain group names or "all".
+        """
+        result = set()
+        for group in value.strip().split():
+            if group == "all":
+                return cls.all_groups()
+            try:
+                result.add(cls(group))
+            except ValueError:
+                msg = f"Unknown enable group: {group}"
+                raise ValueError(msg) from None
+        return frozenset(result)
+
 
 @dataclass(frozen=True, kw_only=True)
 class BuildSelector:
diff --git a/docs/contributing.md b/docs/contributing.md
index 4eea79599..86cd49bdf 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -82,6 +82,8 @@ A few notes-
 
 - Running the macOS integration tests requires _system installs_ of Python from python.org for all the versions that are tested. We won't attempt to install these when running locally, but you can do so manually using the URL in the error message that is printed when the install is not found.
 
+- The 'enable groups' run by default are just 'cpython-prerelease' and 'cpython-freethreading'. You can add other groups like pypy or graalpy by setting the [CIBW_ENABLE](options.md#enable) environment variable. On GitHub PRs, you can add a label to the PR to enable these groups.
+
 #### Running pytest directly
 
 More advanced users might prefer to invoke pytest directly. Set up a [dev environment](#setting-up-a-dev-environment), then,
diff --git a/docs/options.md b/docs/options.md
index 5006d8e28..c36c423ab 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -324,7 +324,7 @@ values are:
   are disabled by default as they can't be uploaded to PyPI and a PEP will most likely
   be required before this can happen.
 - `graalpy`: Enable GraalPy.
-
+- `all`: Enable all of the above.
 
 !!! caution
     `cpython-prerelease` is provided for testing purposes only. It is not
diff --git a/test/conftest.py b/test/conftest.py
index f2b575df0..8c3e15221 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,4 +1,5 @@
 import json
+import os
 import subprocess
 from collections.abc import Generator
 
@@ -12,6 +13,9 @@
 
 from .utils import EMULATED_ARCHS, platform
 
+# default to just cpython
+DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease cpython-experimental-riscv64"
+
 
 def pytest_addoption(parser: pytest.Parser) -> None:
     parser.addoption(
@@ -29,6 +33,8 @@ def pytest_addoption(parser: pytest.Parser) -> None:
         help="macOS cp38 uses the universal2 installer",
     )
 
+    os.environ.setdefault("CIBW_ENABLE", DEFAULT_CIBW_ENABLE)
+
 
 def docker_warmup(request: pytest.FixtureRequest) -> None:
     machine = request.config.getoption("--run-emulation", default=None)
diff --git a/test/test_0_basic.py b/test/test_0_basic.py
index 6a7732031..d0503c105 100644
--- a/test/test_0_basic.py
+++ b/test/test_0_basic.py
@@ -1,8 +1,10 @@
+import os
 import textwrap
 
 import pytest
 
 from cibuildwheel.logger import Logger
+from cibuildwheel.selector import EnableGroup
 
 from . import test_projects, utils
 
@@ -38,11 +40,13 @@ def test(tmp_path, build_frontend_env, capfd):
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
     assert set(actual_wheels) == set(expected_wheels)
 
-    # Verify pip warning not shown
-    captured = capfd.readouterr()
-    for stream in (captured.err, captured.out):
-        assert "WARNING: Running pip as the 'root' user can result" not in stream
-        assert "A new release of pip available" not in stream
+    enable_groups = EnableGroup.parse_option_value(os.environ.get("CIBW_ENABLE", ""))
+    if EnableGroup.GraalPy not in enable_groups:
+        # Verify pip warning not shown
+        captured = capfd.readouterr()
+        for stream in (captured.err, captured.out):
+            assert "WARNING: Running pip as the 'root' user can result" not in stream
+            assert "A new release of pip available" not in stream
 
 
 @pytest.mark.skip(reason="to keep test output clean")
@@ -61,16 +65,19 @@ def test_sample_build(tmp_path, capfd):
             logger.step_end()
 
 
-def test_build_identifiers(tmp_path):
+@pytest.mark.parametrize(
+    "enable_setting", ["", "cpython-prerelease", "pypy", "cpython-freethreading"]
+)
+def test_build_identifiers(tmp_path, enable_setting, monkeypatch):
     project_dir = tmp_path / "project"
     basic_project.generate(project_dir)
 
+    monkeypatch.setenv("CIBW_ENABLE", enable_setting)
+
     # check that the number of expected wheels matches the number of build
     # identifiers
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
-    build_identifiers = utils.cibuildwheel_get_build_identifiers(
-        project_dir, prerelease_pythons=True
-    )
+    build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
     assert len(expected_wheels) == len(build_identifiers), (
         f"{expected_wheels} vs {build_identifiers}"
     )
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
index 5c7c27f54..62f03850f 100644
--- a/test/test_abi_variants.py
+++ b/test/test_abi_variants.py
@@ -35,36 +35,34 @@ def test_abi3(tmp_path):
     project_dir = tmp_path / "project"
     limited_api_project.generate(project_dir)
 
-    single_python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
-
     # build the wheels
     actual_wheels = utils.cibuildwheel_run(
         project_dir,
         add_env={
             # free_threaded, GraalPy, and PyPy do not have a Py_LIMITED_API equivalent, just build one of those
             # also limit the number of builds for test performance reasons
-            "CIBW_BUILD": f"cp39-* cp310-* pp310-* gp242-* {single_python_tag}-* cp313t-*"
+            "CIBW_BUILD": "cp39-* cp310-* pp310-* gp242-* cp312-* cp313t-*",
+            "CIBW_ENABLE": "all",
         },
     )
 
     # check that the expected wheels are produced
-    expected_wheels = utils.expected_wheels("spam", "0.1.0")
     if utils.platform == "pyodide":
-        # there's only 1 possible configuration for pyodide, the single_python_tag one
-        expected_wheels = [
-            w.replace(f"{single_python_tag}-{single_python_tag}", "cp310-abi3")
-            for w in expected_wheels
-        ]
+        # there's only 1 possible configuration for pyodide, cp312
+        expected_wheels = utils.expected_wheels("spam", "0.1.0", python_abi_tags=["cp310-abi3"])
     else:
-        expected_wheels = [
-            w.replace("cp310-cp310", "cp310-abi3")
-            for w in expected_wheels
-            if "-cp39" in w
-            or "-cp310" in w
-            or "-pp310" in w
-            or "-graalpy242" in w
-            or "-cp313t" in w
-        ]
+        expected_wheels = utils.expected_wheels(
+            "spam",
+            "0.1.0",
+            python_abi_tags=[
+                "cp39-cp39",
+                "cp310-abi3",  # <-- ABI3, works with 3.10 and 3.12
+                "cp313-cp313t",
+                "pp310-pypy310_pp73",
+                "graalpy311-graalpy242_311_native",
+            ],
+        )
+
     assert set(actual_wheels) == set(expected_wheels)
 
 
@@ -187,6 +185,7 @@ def test_abi_none(tmp_path, capfd):
             "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} ./test",
             # limit the number of builds for test performance reasons
             "CIBW_BUILD": "cp38-* cp{}{}-* cp313t-* pp310-*".format(*utils.SINGLE_PYTHON_VERSION),
+            "CIBW_ENABLE": "all",
         },
     )
 
diff --git a/test/utils.py b/test/utils.py
index 957ee7295..452d002fb 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -23,6 +23,9 @@
 EMULATED_ARCHS: Final[list[str]] = sorted(
     arch.value for arch in (Architecture.all_archs("linux") - Architecture.auto_archs("linux"))
 )
+PYPY_ARCHS = ["x86_64", "i686", "AMD64", "aarch64", "arm64"]
+GRAALPY_ARCHS = ["x86_64", "AMD64", "aarch64", "arm64"]
+
 SINGLE_PYTHON_VERSION: Final[tuple[int, int]] = (3, 12)
 
 _AARCH64_CAN_RUN_ARMV7: Final[bool] = Architecture.aarch64.value not in EMULATED_ARCHS and {
@@ -46,7 +49,8 @@
 
 
 def cibuildwheel_get_build_identifiers(
-    project_path: Path, env: dict[str, str] | None = None, *, prerelease_pythons: bool = False
+    project_path: Path,
+    env: dict[str, str] | None = None,
 ) -> list[str]:
     """
     Returns the list of build identifiers that cibuildwheel will try to build
@@ -55,9 +59,6 @@ def cibuildwheel_get_build_identifiers(
     cmd = [sys.executable, "-m", "cibuildwheel", "--print-build-identifiers", str(project_path)]
     if env is None:
         env = os.environ.copy()
-    env["CIBW_ENABLE"] = "cpython-freethreading pypy graalpy"
-    if prerelease_pythons:
-        env["CIBW_ENABLE"] += " cpython-prerelease"
 
     cmd_output = subprocess.run(
         cmd,
@@ -121,8 +122,6 @@ def cibuildwheel_run(
 
     _update_pip_cache_dir(env)
 
-    env["CIBW_ENABLE"] = " ".join(EnableGroup.all_groups())
-
     if single_python:
         env["CIBW_BUILD"] = "cp{}{}-*".format(*SINGLE_PYTHON_VERSION)
 
@@ -222,6 +221,8 @@ def _expected_wheels(
     # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
     # so we'll merge them below as python_abi_tag
 
+    enable_groups = EnableGroup.parse_option_value(os.environ.get("CIBW_ENABLE", ""))
+
     if manylinux_versions is None:
         manylinux_versions = {
             "armv7l": ["manylinux_2_17", "manylinux2014", "manylinux_2_31"],
@@ -243,25 +244,36 @@ def _expected_wheels(
             "cp311-cp311",
             "cp312-cp312",
             "cp313-cp313",
-            "cp313-cp313t",
         ]
 
-        if machine_arch == "ARM64":
-            # no CPython 3.8 on Windows ARM64
-            python_abi_tags.pop(0)
+        if EnableGroup.CPythonFreeThreading in enable_groups:
+            python_abi_tags += [
+                "cp313-cp313t",
+            ]
 
-        if machine_arch in ["x86_64", "i686", "AMD64", "aarch64", "arm64"]:
+        if EnableGroup.PyPy in enable_groups:
             python_abi_tags += [
                 "pp38-pypy38_pp73",
                 "pp39-pypy39_pp73",
                 "pp310-pypy310_pp73",
                 "pp311-pypy311_pp73",
             ]
-        if machine_arch in ["x86_64", "AMD64", "aarch64", "arm64"]:
+
+        if EnableGroup.GraalPy in enable_groups:
             python_abi_tags += [
                 "graalpy311-graalpy242_311_native",
             ]
 
+    if machine_arch == "ARM64" and platform == "windows":
+        # no CPython 3.8 on Windows ARM64
+        python_abi_tags = [t for t in python_abi_tags if not t.startswith("cp38")]
+
+    if machine_arch not in PYPY_ARCHS:
+        python_abi_tags = [tag for tag in python_abi_tags if not tag.startswith("pp")]
+
+    if machine_arch not in GRAALPY_ARCHS:
+        python_abi_tags = [tag for tag in python_abi_tags if not tag.startswith("graalpy")]
+
     if single_python:
         python_tag = "cp{}{}-".format(*SINGLE_PYTHON_VERSION)
         python_abi_tags = [
@@ -272,13 +284,6 @@ def _expected_wheels(
             )
         ]
 
-    if platform == "pyodide":
-        assert len(python_abi_tags) == 1
-        python_abi_tag = python_abi_tags[0]
-        platform_tag = "pyodide_2024_0_wasm32"
-        yield f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl"
-        return
-
     for python_abi_tag in python_abi_tags:
         platform_tags = []
 
@@ -327,6 +332,10 @@ def _expected_wheels(
 
             if include_universal2:
                 platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2")
+
+        elif platform == "pyodide":
+            platform_tags = ["pyodide_2024_0_wasm32"]
+
         else:
             msg = f"Unsupported platform {platform!r}"
             raise Exception(msg)
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index 66ee4c952..731d10adc 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -329,6 +329,7 @@ def test_config_settings(platform_specific, platform, intercepted_build_args, mo
 @pytest.mark.usefixtures("platform", "intercepted_build_args", "allow_empty")
 def test_build_selector_deprecated_error(monkeypatch, selector, pattern, capsys):
     monkeypatch.setenv(selector, pattern)
+    monkeypatch.delenv("CIBW_ENABLE", raising=False)
 
     if selector == "CIBW_BUILD":
         with pytest.raises(SystemExit) as ex:
@@ -422,6 +423,8 @@ def test_debug_traceback(monkeypatch, method, capfd):
 
 @pytest.mark.parametrize("method", ["unset", "command_line", "env_var"])
 def test_enable(method, intercepted_build_args, monkeypatch):
+    monkeypatch.delenv("CIBW_ENABLE", raising=False)
+
     if method == "command_line":
         monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "pypy", "--enable", "graalpy"])
     elif method == "env_var":
@@ -437,6 +440,15 @@ def test_enable(method, intercepted_build_args, monkeypatch):
         assert enable_groups == frozenset([EnableGroup.PyPy, EnableGroup.GraalPy])
 
 
+def test_enable_all(intercepted_build_args, monkeypatch):
+    monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "all"])
+
+    main()
+
+    enable_groups = intercepted_build_args.args[0].globals.build_selector.enable
+    assert enable_groups == EnableGroup.all_groups()
+
+
 def test_enable_arg_inherits(intercepted_build_args, monkeypatch):
     monkeypatch.setenv("CIBW_ENABLE", "pypy graalpy")
     monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "cpython-prerelease"])
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index d5dc7d191..3356c523a 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -51,6 +51,7 @@ def ignore_context_call(*args, **kwargs):
 @pytest.mark.usefixtures("mock_build_container", "fake_package_dir")
 def test_build_default_launches(monkeypatch):
     monkeypatch.setattr(sys, "argv", [*sys.argv, "--platform=linux"])
+    monkeypatch.delenv("CIBW_ENABLE", raising=False)
 
     main()
 

From 5b65230f6438c3e15f0c672d2be51b3b5eac3781 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 8 May 2025 12:12:14 -0400
Subject: [PATCH 060/193] fix: virtualenv 20.31 no-wheel (#2382)

* fix: virtualenv 20.31 no-wheel

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: show failed output on failure

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update linux.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update linux.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 cibuildwheel/platforms/linux.py |  4 +++-
 cibuildwheel/venv.py            | 10 ++++++----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index e39a24e08..28650525e 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -353,7 +353,9 @@ def build_in_container(
                 container.call(["uv", "venv", venv_dir, "--python", python_bin / "python"], env=env)
             else:
                 # Use embedded dependencies from virtualenv to ensure determinism
-                venv_args = ["--no-periodic-update", "--pip=embed", "--no-setuptools", "--no-wheel"]
+                venv_args = ["--no-periodic-update", "--pip=embed", "--no-setuptools"]
+                if "38" in config.identifier:
+                    venv_args.append("--no-wheel")
                 container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)
 
             virtualenv_env = env.copy()
diff --git a/cibuildwheel/venv.py b/cibuildwheel/venv.py
index 77624b689..567ba4001 100644
--- a/cibuildwheel/venv.py
+++ b/cibuildwheel/venv.py
@@ -20,7 +20,7 @@
 
 
 @functools.cache
-def _ensure_virtualenv(version: str) -> Path:
+def _ensure_virtualenv(version: str) -> tuple[Path, Version]:
     version_parts = version.split(".")
     key = f"py{version_parts[0]}{version_parts[1]}"
     with resources.VIRTUALENV.open("rb") as f:
@@ -32,7 +32,7 @@ def _ensure_virtualenv(version: str) -> Path:
     with FileLock(str(path) + ".lock"):
         if not path.exists():
             download(url, path)
-    return path
+    return (path, Version(version))
 
 
 def constraint_flags(
@@ -110,10 +110,12 @@ def virtualenv(
     if use_uv:
         call("uv", "venv", venv_path, "--python", python)
     else:
-        virtualenv_app = _ensure_virtualenv(version)
+        virtualenv_app, virtualenv_version = _ensure_virtualenv(version)
         if pip_version is None:
             pip_version = _parse_pip_constraint_for_virtualenv(dependency_constraint)
-        additional_flags = [f"--pip={pip_version}", "--no-setuptools", "--no-wheel"]
+        additional_flags = [f"--pip={pip_version}", "--no-setuptools"]
+        if virtualenv_version < Version("20.31") or Version(version) < Version("3.9"):
+            additional_flags.append("--no-wheel")
 
         # Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
         # environment. The initial cost is a bit higher but reusing is much faster.

From 54a4dccd623a91f5dbb47327370f85132c1e9535 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sun, 11 May 2025 10:16:54 +0100
Subject: [PATCH 061/193] Remove official Appveyor support (#2386)

---
 CI.md                               | 10 ++++----
 README.md                           |  7 ++----
 appveyor.yml                        | 38 -----------------------------
 bin/projects.py                     |  1 -
 bin/run_example_ci_configs.py       |  5 ----
 cibuildwheel/ci.py                  |  6 ++++-
 docs/ci-services.md                 | 22 +++++------------
 docs/data/projects.schema.json      |  1 -
 docs/data/projects.yml              | 12 ++++-----
 docs/data/readme_icons/appveyor.svg |  1 -
 docs/working-examples.md            |  5 ++--
 examples/appveyor-minimal.yml       | 18 --------------
 pyproject.toml                      |  1 -
 test/test_cpp_standards.py          |  6 -----
 14 files changed, 26 insertions(+), 107 deletions(-)
 delete mode 100644 appveyor.yml
 delete mode 100644 docs/data/readme_icons/appveyor.svg
 delete mode 100644 examples/appveyor-minimal.yml

diff --git a/CI.md b/CI.md
index d1282bc36..6b45fffc3 100644
--- a/CI.md
+++ b/CI.md
@@ -1,10 +1,10 @@
 This is a summary of the host Python versions and platforms covered by the different CI platforms:
 
-|         | 3.11                             | 3.12                                                    | 3.13           |
-|---------|----------------------------------|---------------------------------------------------------|----------------|
-| Linux   | Azure Pipelines / GitHub Actions | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ / Travis CI | GitHub Actions |
-| macOS   | Azure Pipelines                  | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹             | GitHub Actions |
-| Windows | Azure Pipelines                  | AppVeyor¹ / Cirrus CI / GitLab¹ / Travis CI             | GitHub Actions |
+|         | 3.11                             | 3.12                                        | 3.13           |
+|---------|----------------------------------|---------------------------------------------|----------------|
+| Linux   | Azure Pipelines / GitHub Actions | CircleCI¹ / Cirrus CI / GitLab¹ / Travis CI | GitHub Actions |
+| macOS   | Azure Pipelines                  | CircleCI¹ / Cirrus CI / GitLab¹             | GitHub Actions |
+| Windows | Azure Pipelines                  | Cirrus CI / GitLab¹ / Travis CI             | GitHub Actions |
 
 > ¹ Runs a reduced set of tests to reduce CI load
 
diff --git a/README.md b/README.md
index 2d9af6b11..d9f258e38 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@ cibuildwheel
 [![Documentation Status](https://readthedocs.org/projects/cibuildwheel/badge/?version=stable)](https://cibuildwheel.pypa.io/en/stable/?badge=stable)
 [![Actions Status](https://github.com/pypa/cibuildwheel/workflows/Test/badge.svg)](https://github.com/pypa/cibuildwheel/actions)
 [![Travis Status](https://img.shields.io/travis/com/pypa/cibuildwheel/main?logo=travis)](https://travis-ci.com/github/pypa/cibuildwheel)
-[![Appveyor status](https://ci.appveyor.com/api/projects/status/gt3vwl88yt0y3hur/branch/main?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/main)
 [![CircleCI Status](https://img.shields.io/circleci/build/gh/pypa/cibuildwheel/main?logo=circleci)](https://circleci.com/gh/pypa/cibuildwheel)
 [![Azure Status](https://dev.azure.com/joerick0429/cibuildwheel/_apis/build/status/pypa.cibuildwheel?branchName=main)](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=4&branchName=main)
 
@@ -16,7 +15,7 @@ cibuildwheel
 
 Python wheels are great. Building them across **Mac, Linux, Windows**, on **multiple versions of Python**, is not.
 
-`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, and GitLab CI - and it builds and tests your wheels across all of your platforms.
+`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, Travis CI, CircleCI, and GitLab CI - and it builds and tests your wheels across all of your platforms.
 
 
 What does it do?
@@ -45,7 +44,7 @@ While cibuildwheel itself requires a recent Python version to run (we support th
 <sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br>
 
 - Builds manylinux, musllinux, macOS 10.9+ (10.13+ for Python 3.12+), and Windows wheels for CPython, PyPy, and GraalPy
-- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
+- Works on GitHub Actions, Azure Pipelines, Travis CI, CircleCI, GitLab CI, and Cirrus CI
 - Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
 - Runs your library's tests against the wheel-installed version of your library
 
@@ -61,7 +60,6 @@ Usage
 | GitHub Actions  | ✅    | ✅    | ✅       | ✅        | ✅        | ✅²         | ✅³  |
 | Azure Pipelines | ✅    | ✅    | ✅       |           | ✅        | ✅²         | ✅³  |
 | Travis CI       | ✅    |       | ✅      | ✅        |           |             |     |
-| AppVeyor        | ✅    | ✅    | ✅      |           | ✅        | ✅²         | ✅³  |
 | CircleCI        | ✅    | ✅    |         | ✅        | ✅        |             | ✅³  |
 | Gitlab CI       | ✅    | ✅    | ✅      | ✅¹       | ✅        |             | ✅³  |
 | Cirrus CI       | ✅    | ✅    | ✅      | ✅        | ✅        |             | ✅³  |
@@ -188,7 +186,6 @@ Here are some repos that use cibuildwheel.
 [Prophet]: https://github.com/facebook/prophet
 [Kivy]: https://github.com/kivy/kivy
 
-[appveyor icon]: docs/data/readme_icons/appveyor.svg
 [github icon]: docs/data/readme_icons/github.svg
 [azurepipelines icon]: docs/data/readme_icons/azurepipelines.svg
 [circleci icon]: docs/data/readme_icons/circleci.svg
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index ca5d4a6c4..000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-environment:
-  matrix:
-    - APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2204
-      APPVEYOR_JOB_NAME: "python312-x64-ubuntu"
-    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
-      APPVEYOR_JOB_NAME: "python312-x64-vs2022"
-    - APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma
-      APPVEYOR_JOB_NAME: "python312-x64-macos"
-
-stack: python 3.12
-
-build: off
-
-init:
-- ps: |
-      $BRANCH = if ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH) { $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH } else { $env:APPVEYOR_REPO_BRANCH }
-      if (-not ($BRANCH -eq 'main' -or $BRANCH.ToLower().StartsWith('appveyor-'))) {
-        $env:PYTEST_ADDOPTS = '-k "unit_test or test_0_basic" --suppress-no-test-exit-code'
-      }
-      if ($IsLinux) {
-        docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
-      }
-
-install:
-  - python -m pip install -U pip
-  - python -m pip install -e ".[dev]" pytest-custom-exit-code
-
-# the '-u' flag is required so the output is in the correct order.
-# See https://github.com/pypa/cibuildwheel/pull/24 for more info.
-test_script: python -u ./bin/run_tests.py
-
-branches:
-  only:
-    - main
-
-skip_commits:
-  files:
-    - docs/*
diff --git a/bin/projects.py b/bin/projects.py
index 7414be963..029ef6679 100755
--- a/bin/projects.py
+++ b/bin/projects.py
@@ -33,7 +33,6 @@
 from github import Github, GithubException
 
 ICONS = (
-    "appveyor",
     "github",
     "azurepipelines",
     "circleci",
diff --git a/bin/run_example_ci_configs.py b/bin/run_example_ci_configs.py
index c7a5b8718..a6bd1bd06 100755
--- a/bin/run_example_ci_configs.py
+++ b/bin/run_example_ci_configs.py
@@ -41,11 +41,6 @@ class CIService(typing.NamedTuple):
 
 
 services = [
-    CIService(
-        name="appveyor",
-        dst_config_path="appveyor.yml",
-        badge_md="[![Build status](https://ci.appveyor.com/api/projects/status/gt3vwl88yt0y3hur/branch/{branch}?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/{branch})",
-    ),
     CIService(
         name="azure-pipelines",
         dst_config_path="azure-pipelines.yml",
diff --git a/cibuildwheel/ci.py b/cibuildwheel/ci.py
index a7ba04ee1..03205b7a4 100644
--- a/cibuildwheel/ci.py
+++ b/cibuildwheel/ci.py
@@ -6,13 +6,17 @@
 
 
 class CIProvider(Enum):
+    # official support
     travis_ci = "travis"
-    appveyor = "appveyor"
     circle_ci = "circle_ci"
     azure_pipelines = "azure_pipelines"
     github_actions = "github_actions"
     gitlab = "gitlab"
     cirrus_ci = "cirrus_ci"
+
+    # unofficial support
+    appveyor = "appveyor"
+
     other = "other"
 
 
diff --git a/docs/ci-services.md b/docs/ci-services.md
index ba0c53ec8..6564052f8 100644
--- a/docs/ci-services.md
+++ b/docs/ci-services.md
@@ -86,22 +86,6 @@ Then setup a deployment method by following the [Travis CI deployment docs](http
 
 [`examples/travis-ci-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/travis-ci-deploy.yml) extends this minimal example with a demonstration of how to automatically upload the built wheels to PyPI.
 
-### AppVeyor [linux/mac/windows] {: #appveyor}
-
-To build Linux, Mac, and Windows wheels on AppVeyor, create an `appveyor.yml` file in your repo.
-
-> appveyor.yml
-
-```yaml
-{% include "../examples/appveyor-minimal.yml" %}
-```
-
-Commit this file, enable building of your repo on AppVeyor, and push.
-
-AppVeyor will store the built wheels for you - you can access them from the project console. Alternatively, you may want to store them in the same place as the Travis CI build. See [AppVeyor deployment docs](https://www.appveyor.com/docs/deployment/) for more info, or see [Delivering to PyPI](deliver-to-pypi.md) below.
-
-For more info on this config file, check out the [docs](https://www.appveyor.com/docs/).
-
 ### CircleCI [linux/mac] {: #circleci}
 
 To build Linux and Mac wheels on CircleCI, create a `.circleci/config.yml` file in your repo,
@@ -149,6 +133,12 @@ Cirrus CI will store the built wheels for you - you can access them from the ind
 
 > ⚠️ Got an error? Check the [FAQ](faq.md).
 
+### Other CI services
+
+#### AppVeyor {: #appveyor}
+
+Appveyor official support was dropped in cibuildwheel v3.0, due to a lack of CI credits. However, it can probably still be used as-is. Check the Appveyor example from the cibuildwheel v2.0 branch: [appveyor-minimal.yml](https://github.com/pypa/cibuildwheel/blob/v2.23.3/examples/appveyor-minimal.yml).
+
 ## Next steps
 
 Once you've got the wheel building successfully, you might want to set up [testing](options.md#test-command) or [automatic releases to PyPI](deliver-to-pypi.md#automatic-method).
diff --git a/docs/data/projects.schema.json b/docs/data/projects.schema.json
index ba12ccb9e..452b52c52 100644
--- a/docs/data/projects.schema.json
+++ b/docs/data/projects.schema.json
@@ -18,7 +18,6 @@
           "enum": [
             "github",
             "travisci",
-            "appveyor",
             "circleci",
             "gitlab",
             "cirrusci",
diff --git a/docs/data/projects.yml b/docs/data/projects.yml
index 8c9315bd9..e6ce488d9 100644
--- a/docs/data/projects.yml
+++ b/docs/data/projects.yml
@@ -4,7 +4,7 @@
 # stars: GitHub repo (optional, if different from package, such as for Twisted)
 # pypi: The pypi name, if different from the GitHub package name
 # os: Operating system list, [windows, apple, linux] (optional)
-# ci: [appveyor, github, azurepipelines, circleci, gitlab, travisci, cirrusci] (optional)
+# ci: [github, azurepipelines, circleci, gitlab, travisci, cirrusci] (optional)
 # notes: (text, optional)
 
 - name: abess
@@ -19,11 +19,11 @@
   ci: [github]
   os: [windows, apple, linux]
 
-- name: pyinstrument_cext
-  gh: joerick/pyinstrument_cext
-  ci: [travisci, appveyor]
+- name: pyinstrument
+  gh: joerick/pyinstrument
+  ci: [github]
   os: [windows, apple, linux]
-  notes: A simple C extension, without external dependencies
+  notes: Python profiler with a C extension. No external dependencies.
 
 - name: websockets
   gh: python-websockets/websockets
@@ -182,7 +182,7 @@
 
 - name: python-rapidjson
   gh: python-rapidjson/python-rapidjson
-  ci: [travisci, gitlab, appveyor]
+  ci: [travisci, gitlab]
   os: [windows, linux]
 
 - name: jq.py
diff --git a/docs/data/readme_icons/appveyor.svg b/docs/data/readme_icons/appveyor.svg
deleted file mode 100644
index 6fa0bac27..000000000
--- a/docs/data/readme_icons/appveyor.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24" width="16px" fill="#606060"><title>AppVeyor icon</title><path d="M 12,0 C 18.6,0 24,5.4 24,12 24,18.6 18.6,24 12,24 5.4,24 0,18.6 0,12 0,5.4 5.4,0 12,0 Z m 2.94,14.34 C 16.26,12.66 16.08,10.26 14.4,9 12.78,7.74 10.38,8.04 9,9.72 7.68,11.4 7.86,13.8 9.54,15.06 c 1.68,1.26 4.08,0.96 5.4,-0.72 z m -6.42,7.8 c 0.72,0.3 2.28,0.6 3.06,0.6 l 5.22,-7.56 c 1.68,-2.52 1.26,-5.94 -1.08,-7.8 -2.1,-1.68 -5.04,-1.62 -7.14,0 l -7.26,5.58 c 0.18,1.92 0.72,2.88 0.72,2.94 l 4.14,-4.5 c -0.3,1.98 0.42,4.02 2.1,5.28 1.44,1.14 3.18,1.44 4.86,1.08 z"/></svg>
\ No newline at end of file
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 366d6f257..9f254988f 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -75,7 +75,7 @@ title: Working examples
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
-| [python-rapidjson][]              | ![travisci icon][] ![gitlab icon][] ![appveyor icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson |
+| [python-rapidjson][]              | ![travisci icon][] ![gitlab icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson |
 | [pybind11 python_example][]       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
 | [sourmash][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
 | [abess][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
@@ -115,7 +115,7 @@ title: Working examples
 | [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
 | [xmlstarlet][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. |
 | [werpy][]                         | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | An ultra-fast python package using optimized dynamic programming to compute the Word Error Rate (WER). |
-| [pyinstrument_cext][]             | ![travisci icon][] ![appveyor icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A simple C extension, without external dependencies |
+| [pyinstrument_cext][]             | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A simple C extension, without external dependencies |
 | [pybind11 cross build example][]  | ![github icon][] ![gitlab icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Same as pybind11 cmake_example but used to demo Linux ARM + Windows + macOS builds on GitLab |
 
 [scikit-learn]: https://github.com/scikit-learn/scikit-learn
@@ -228,7 +228,6 @@ title: Working examples
 [pyinstrument_cext]: https://github.com/joerick/pyinstrument_cext
 [pybind11 cross build example]: https://github.com/wbarnha/pybind_cmake_example_crossbuild
 
-[appveyor icon]: data/readme_icons/appveyor.svg
 [github icon]: data/readme_icons/github.svg
 [azurepipelines icon]: data/readme_icons/azurepipelines.svg
 [circleci icon]: data/readme_icons/circleci.svg
diff --git a/examples/appveyor-minimal.yml b/examples/appveyor-minimal.yml
deleted file mode 100644
index 446c2c912..000000000
--- a/examples/appveyor-minimal.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-environment:
-  matrix:
-    - APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2204
-      APPVEYOR_JOB_NAME: "linux-x64"
-    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
-      APPVEYOR_JOB_NAME: "windows-x64"
-    - APPVEYOR_BUILD_WORKER_IMAGE: macos-sonoma
-      APPVEYOR_JOB_NAME: "macos-x64"
-
-stack: python 3.12
-
-install: python -m pip install cibuildwheel==2.23.3
-
-build_script: python -m cibuildwheel --output-dir wheelhouse
-
-artifacts:
-  - path: "wheelhouse\\*.whl"
-    name: Wheels
diff --git a/pyproject.toml b/pyproject.toml
index 9ab482337..1495b9b32 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,6 @@ authors = [
     { name = "Joe Rickerby", email = "joerick@mac.com" },
 ]
 keywords = [
-    "appveyor",
     "ci",
     "linux",
     "macos",
diff --git a/test/test_cpp_standards.py b/test/test_cpp_standards.py
index bb884ad8e..85fcb6d96 100644
--- a/test/test_cpp_standards.py
+++ b/test/test_cpp_standards.py
@@ -1,7 +1,4 @@
-import os
-
 import jinja2
-import pytest
 
 from . import utils
 from .test_projects import TestProject
@@ -100,9 +97,6 @@ def test_cpp17(tmp_path):
     """
     cpp17_project.generate(project_dir)
 
-    if os.environ.get("APPVEYOR_BUILD_WORKER_IMAGE", "") == "Visual Studio 2015":
-        pytest.skip("Visual Studio 2015 does not support C++17")
-
     add_env = {}
     if utils.platform == "macos":
         add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.13"

From 5622cc698811beace37bd8c8a4419bee81f11a9f Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 12 May 2025 18:54:02 +0200
Subject: [PATCH 062/193] Add Python 3.14 preview (#2390)

* feat: add CPython 3.14 beta 1

* fix tests
---
 README.md                                     |  1 +
 cibuildwheel/platforms/macos.py               |  2 +
 cibuildwheel/resources/build-platforms.toml   | 40 ++++++++++++++
 .../resources/constraints-pyodide312.txt      |  6 +--
 .../resources/constraints-python310.txt       |  4 +-
 .../resources/constraints-python311.txt       |  4 +-
 .../resources/constraints-python312.txt       |  4 +-
 .../resources/constraints-python313.txt       |  4 +-
 .../resources/constraints-python314.txt       | 28 ++++++++++
 .../resources/constraints-python38.txt        |  2 +-
 .../resources/constraints-python39.txt        |  4 +-
 cibuildwheel/resources/constraints.txt        |  4 +-
 .../resources/free-threaded-enable-314.xml    | 14 +++++
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 cibuildwheel/resources/virtualenv.toml        |  2 +-
 cibuildwheel/util/resources.py                |  1 +
 docs/options.md                               |  1 +
 noxfile.py                                    |  4 +-
 test/test_emulation.py                        |  2 +-
 test/utils.py                                 | 11 ++--
 unit_test/option_prepare_test.py              |  1 +
 21 files changed, 142 insertions(+), 51 deletions(-)
 create mode 100644 cibuildwheel/resources/constraints-python314.txt
 create mode 100644 cibuildwheel/resources/free-threaded-enable-314.xml

diff --git a/README.md b/README.md
index d9f258e38..21049c093 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ While cibuildwheel itself requires a recent Python version to run (we support th
 | CPython 3.11   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
 | CPython 3.12   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | ✅⁴ |
 | CPython 3.13³  | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
+| CPython 3.14³  | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
 | PyPy 3.8 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
 | PyPy 3.9 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
 | PyPy 3.10 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 420e16139..e2f5b9115 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -158,6 +158,8 @@ def install_cpython(_tmp: Path, version: str, url: str, free_threading: bool) ->
             if version.startswith("3.13"):
                 # Python 3.13 is the first version to have a free-threading option
                 args += ["-applyChoiceChangesXML", str(resources.FREE_THREAD_ENABLE_313.resolve())]
+            elif version.startswith("3.14"):
+                args += ["-applyChoiceChangesXML", str(resources.FREE_THREAD_ENABLE_314.resolve())]
             call("sudo", "installer", "-pkg", pkg_path, *args, "-target", "/")
             pkg_path.unlink()
             env = os.environ.copy()
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 15f534022..973154586 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -7,6 +7,8 @@ python_configurations = [
   { identifier = "cp312-manylinux_x86_64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-manylinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-manylinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-manylinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-manylinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-manylinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -14,6 +16,8 @@ python_configurations = [
   { identifier = "cp312-manylinux_i686", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-manylinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-manylinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "pp38-manylinux_x86_64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
   { identifier = "pp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
@@ -26,6 +30,8 @@ python_configurations = [
   { identifier = "cp312-manylinux_aarch64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-manylinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-manylinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-manylinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-manylinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-manylinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -33,6 +39,8 @@ python_configurations = [
   { identifier = "cp312-manylinux_ppc64le", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-manylinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-manylinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-manylinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-manylinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-manylinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -40,6 +48,8 @@ python_configurations = [
   { identifier = "cp312-manylinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-manylinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-manylinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-manylinux_armv7l", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-manylinux_armv7l", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-manylinux_armv7l", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -47,6 +57,8 @@ python_configurations = [
   { identifier = "cp312-manylinux_armv7l", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-manylinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-manylinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-manylinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-manylinux_riscv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-manylinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -54,6 +66,8 @@ python_configurations = [
   { identifier = "cp312-manylinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-manylinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-manylinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-manylinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-manylinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "pp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
   { identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
@@ -70,6 +84,8 @@ python_configurations = [
   { identifier = "cp312-musllinux_x86_64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-musllinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-musllinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-musllinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-musllinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-musllinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -77,6 +93,8 @@ python_configurations = [
   { identifier = "cp312-musllinux_i686", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-musllinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-musllinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-musllinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-musllinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-musllinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -84,6 +102,8 @@ python_configurations = [
   { identifier = "cp312-musllinux_aarch64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-musllinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-musllinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-musllinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-musllinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-musllinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -91,6 +111,8 @@ python_configurations = [
   { identifier = "cp312-musllinux_ppc64le", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-musllinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-musllinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-musllinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-musllinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-musllinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -98,6 +120,8 @@ python_configurations = [
   { identifier = "cp312-musllinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-musllinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-musllinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-musllinux_armv7l", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-musllinux_armv7l", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-musllinux_armv7l", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -105,6 +129,8 @@ python_configurations = [
   { identifier = "cp312-musllinux_armv7l", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-musllinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-musllinux_ricv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-musllinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -112,6 +138,8 @@ python_configurations = [
   { identifier = "cp312-musllinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
   { identifier = "cp313-musllinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
   { identifier = "cp313t-musllinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
+  { identifier = "cp314-musllinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
+  { identifier = "cp314t-musllinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
 ]
 
 [macos]
@@ -137,6 +165,12 @@ python_configurations = [
   { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
   { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
   { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
+  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
+  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
+  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
+  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
+  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
   { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
   { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
   { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
@@ -165,12 +199,18 @@ python_configurations = [
   { identifier = "cp313t-win32", version = "3.13.3", arch = "32" },
   { identifier = "cp313-win_amd64", version = "3.13.3", arch = "64" },
   { identifier = "cp313t-win_amd64", version = "3.13.3", arch = "64" },
+  { identifier = "cp314-win32", version = "3.14.0-b1", arch = "32" },
+  { identifier = "cp314t-win32", version = "3.14.0-b1", arch = "32" },
+  { identifier = "cp314-win_amd64", version = "3.14.0-b1", arch = "64" },
+  { identifier = "cp314t-win_amd64", version = "3.14.0-b1", arch = "64" },
   { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
   { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
   { identifier = "cp313-win_arm64", version = "3.13.3", arch = "ARM64" },
   { identifier = "cp313t-win_arm64", version = "3.13.3", arch = "ARM64" },
+  { identifier = "cp314-win_arm64", version = "3.14.0-b1", arch = "ARM64" },
+  { identifier = "cp314t-win_arm64", version = "3.14.0-b1", arch = "ARM64" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 2580c967b..bfe80a4ac 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -19,7 +19,7 @@ charset-normalizer==3.4.2
     # via requests
 click==8.1.8
     # via typer
-cmake==4.0.0
+cmake==4.0.2
     # via pyodide-build
 distlib==0.3.9
     # via virtualenv
@@ -50,7 +50,7 @@ packaging==25.0
     #   unearth
 pip==25.1.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
-platformdirs==4.3.7
+platformdirs==4.3.8
     # via virtualenv
 pydantic==2.11.4
     # via
@@ -105,7 +105,7 @@ unearth==0.17.5
     # via pyodide-build
 urllib3==2.4.0
     # via requests
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via
     #   build
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 3f3ca6dca..5883ddfe0 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -20,7 +20,7 @@ packaging==25.0
     #   delocate
 pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.7
+platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index b3f0cf64c..0cff478d6 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -18,11 +18,11 @@ packaging==25.0
     #   delocate
 pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.7
+platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index b3f0cf64c..0cff478d6 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -18,11 +18,11 @@ packaging==25.0
     #   delocate
 pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.7
+platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index b3f0cf64c..0cff478d6 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -18,11 +18,11 @@ packaging==25.0
     #   delocate
 pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.7
+platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python314.txt b/cibuildwheel/resources/constraints-python314.txt
new file mode 100644
index 000000000..0cff478d6
--- /dev/null
+++ b/cibuildwheel/resources/constraints-python314.txt
@@ -0,0 +1,28 @@
+# This file was autogenerated by uv via the following command:
+#    nox -s update_constraints
+altgraph==0.17.4
+    # via macholib
+build==1.2.2.post1
+    # via -r cibuildwheel/resources/constraints.in
+delocate==0.13.0
+    # via -r cibuildwheel/resources/constraints.in
+distlib==0.3.9
+    # via virtualenv
+filelock==3.18.0
+    # via virtualenv
+macholib==1.16.3
+    # via delocate
+packaging==25.0
+    # via
+    #   build
+    #   delocate
+pip==25.1.1
+    # via -r cibuildwheel/resources/constraints.in
+platformdirs==4.3.8
+    # via virtualenv
+pyproject-hooks==1.2.0
+    # via build
+typing-extensions==4.13.2
+    # via delocate
+virtualenv==20.31.2
+    # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 490e373a9..2c40131f4 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.20.2
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 3f3ca6dca..5883ddfe0 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -20,7 +20,7 @@ packaging==25.0
     #   delocate
 pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.7
+platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.21.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index b3f0cf64c..0cff478d6 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -18,11 +18,11 @@ packaging==25.0
     #   delocate
 pip==25.1.1
     # via -r cibuildwheel/resources/constraints.in
-platformdirs==4.3.7
+platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.30.0
+virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/free-threaded-enable-314.xml b/cibuildwheel/resources/free-threaded-enable-314.xml
new file mode 100644
index 000000000..88fa8dd98
--- /dev/null
+++ b/cibuildwheel/resources/free-threaded-enable-314.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+        <dict>
+                <key>attributeSetting</key>
+                <integer>1</integer>
+                <key>choiceAttribute</key>
+                <string>selected</string>
+                <key>choiceIdentifier</key>
+                <string>org.python.Python.PythonTFramework-3.14</string>
+        </dict>
+</array>
+</plist>
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 3d1b3283c..1ff2bef8d 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.03-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.03-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.26-0
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.03-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.10-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.10-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.10-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.10-2
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.03-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.03-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.10-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.10-2
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.03-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.03-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.26-0
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.03-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.10-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.10-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.10-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.10-2
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.03-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.03-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.04.26-0
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.03-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.10-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.10-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.10-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.10-2
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.03-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.03-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.04.26-0
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.03-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.10-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.10-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.10-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.10-2
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.03-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.03-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.04.26-0
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.10-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.10-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.10-2
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.03-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.10-2
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.03-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.03-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.04.26-0
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.10-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.10-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.10-2
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.03-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.03-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.10-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.10-2
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index e9b284617..9e5d08c24 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1 +1 @@
-default = { version = "20.30.0", url = "https://github.com/pypa/get-virtualenv/blob/20.30.0/public/virtualenv.pyz?raw=true" }
+default = { version = "20.31.2", url = "https://github.com/pypa/get-virtualenv/blob/20.31.2/public/virtualenv.pyz?raw=true" }
diff --git a/cibuildwheel/util/resources.py b/cibuildwheel/util/resources.py
index e27f1f892..40e085d68 100644
--- a/cibuildwheel/util/resources.py
+++ b/cibuildwheel/util/resources.py
@@ -8,6 +8,7 @@
 PATH: Final[Path] = Path(__file__).parent.parent / "resources"
 INSTALL_CERTIFI_SCRIPT: Final[Path] = PATH / "install_certifi.py"
 FREE_THREAD_ENABLE_313: Final[Path] = PATH / "free-threaded-enable-313.xml"
+FREE_THREAD_ENABLE_314: Final[Path] = PATH / "free-threaded-enable-314.xml"
 NODEJS: Final[Path] = PATH / "nodejs.toml"
 DEFAULTS: Final[Path] = PATH / "defaults.toml"
 PINNED_DOCKER_IMAGES: Final[Path] = PATH / "pinned_docker_images.cfg"
diff --git a/docs/options.md b/docs/options.md
index c36c423ab..1a79d3750 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -58,6 +58,7 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
 | Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                                                                   |
 | Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                                                                   |
 | Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator |
+| Python 3.14   | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 |                                                                                                   |
 | PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |
 | PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |
 | PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |
diff --git a/noxfile.py b/noxfile.py
index 356bb8946..b018a952d 100755
--- a/noxfile.py
+++ b/noxfile.py
@@ -73,7 +73,7 @@ def update_constraints(session: nox.Session) -> None:
     if session.venv_backend != "uv":
         session.install("uv>=0.1.23")
 
-    for minor_version in range(8, 14):
+    for minor_version in range(8, 15):
         python_version = f"3.{minor_version}"
         env = os.environ.copy()
         # CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
@@ -92,7 +92,7 @@ def update_constraints(session: nox.Session) -> None:
         )
 
     shutil.copyfile(
-        resources / "constraints-python312.txt",
+        resources / "constraints-python314.txt",
         resources / "constraints.txt",
     )
 
diff --git a/test/test_emulation.py b/test/test_emulation.py
index 0d218b000..9ffdc6de8 100644
--- a/test/test_emulation.py
+++ b/test/test_emulation.py
@@ -35,7 +35,7 @@ def test(tmp_path, request):
             "CIBW_TEST_COMMAND": "pytest ./test",
             "CIBW_ARCHS": archs,
             # TODO remove me once proper support is added
-            "CIBW_MANYLINUX_RISCV64_IMAGE": "ghcr.io/mayeut/manylinux_2_31:2025.03.02-1",
+            "CIBW_MANYLINUX_RISCV64_IMAGE": "ghcr.io/mayeut/manylinux_2_35:2025.05.11-1",
             "CIBW_SKIP": "*-musllinux_riscv64",
         },
     )
diff --git a/test/utils.py b/test/utils.py
index 452d002fb..7e566f503 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -228,7 +228,7 @@ def _expected_wheels(
             "armv7l": ["manylinux_2_17", "manylinux2014", "manylinux_2_31"],
             "i686": ["manylinux_2_5", "manylinux1", "manylinux_2_17", "manylinux2014"],
             "x86_64": ["manylinux_2_5", "manylinux1", "manylinux_2_28"],
-            "riscv64": ["manylinux_2_31"],
+            "riscv64": ["manylinux_2_31", "manylinux_2_35"],
         }.get(machine_arch, ["manylinux_2_17", "manylinux2014", "manylinux_2_28"])
 
     if musllinux_versions is None:
@@ -247,9 +247,12 @@ def _expected_wheels(
         ]
 
         if EnableGroup.CPythonFreeThreading in enable_groups:
-            python_abi_tags += [
-                "cp313-cp313t",
-            ]
+            python_abi_tags.append("cp313-cp313t")
+
+        if EnableGroup.CPythonPrerelease in enable_groups:
+            python_abi_tags.append("cp314-cp314")
+            if EnableGroup.CPythonFreeThreading in enable_groups:
+                python_abi_tags.append("cp314-cp314t")
 
         if EnableGroup.PyPy in enable_groups:
             python_abi_tags += [
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 3356c523a..09ff57525 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -120,6 +120,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 
     monkeypatch.chdir(pkg_dir)
     monkeypatch.setattr(sys, "argv", ["cibuildwheel", "--platform=linux"])
+    monkeypatch.delenv("CIBW_ENABLE", raising=False)
 
     main()
 

From e7dcb31aa7a1b6b9e7fc4384df89217bbe8a5c94 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 13 May 2025 01:28:55 -0400
Subject: [PATCH 063/193] [pre-commit.ci] pre-commit autoupdate (#2392)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.8 → v0.11.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.8...v0.11.9)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 41be3c2a2..7d2c65182 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.8
+  rev: v0.11.9
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From d193a8eff5d1ae3a95278a090f85336657817229 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Wed, 14 May 2025 10:06:34 -0400
Subject: [PATCH 064/193] tests: fix ci (#2391)

* Update dependencies

* tests: disable color for Pyodide test

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: nist.gov doesn't work anymore for this test

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: limit click for typer

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/constraints-pyodide312.txt |  4 +++-
 docs/working-examples.md                          | 12 ++++++------
 noxfile.py                                        |  4 +++-
 test/test_build_frontend_args.py                  |  1 +
 test/test_ssl.py                                  |  2 +-
 5 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index bfe80a4ac..5e1c9ca5c 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -18,7 +18,9 @@ certifi==2025.4.26
 charset-normalizer==3.4.2
     # via requests
 click==8.1.8
-    # via typer
+    # via
+    #   -r .nox/update_constraints/tmp/constraints-pyodide.in
+    #   typer
 cmake==4.0.2
     # via pyodide-build
 distlib==0.3.9
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 9f254988f..825b516e9 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -28,6 +28,7 @@ title: Working examples
 | [PyGame][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | 🐍🎮 pygame (the library) is a Free and Open Source python programming language library for making multimedia applications like games built on top of the excellent SDL library. C, Python, Native, OpenGL. |
 | [asyncpg][]                       | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A fast PostgreSQL Database Client Library for Python/asyncio. |
 | [cmake][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
+| [pyinstrument][]                  | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python profiler with a C extension. No external dependencies. |
 | [scikit-image][]                  | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Image processing library. Uses cibuildwheel to build and test a project that uses Cython with platform-native code.  |
 | [PyOxidizer][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool |
 | [twisted-iocpsupport][]           | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
@@ -98,14 +99,14 @@ title: Working examples
 | [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
 | [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
-| [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
 | [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
+| [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
 | [Arbor][]                         | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries.  |
 | [clang-format][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. |
 | [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
-| [cf-units][]                      | ![github icon][] | ![apple icon][] ![linux icon][] | Units of measure as required by the Climate and Forecast (CF) Metadata Conventions |
 | [etebase-py][]                    | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. |
 | [ninja][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
+| [cf-units][]                      | ![github icon][] | ![apple icon][] ![linux icon][] | Units of measure as required by the Climate and Forecast (CF) Metadata Conventions |
 | [numpythia][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
 | [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
 | [ril][]                           | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
@@ -115,7 +116,6 @@ title: Working examples
 | [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
 | [xmlstarlet][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. |
 | [werpy][]                         | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | An ultra-fast python package using optimized dynamic programming to compute the Word Error Rate (WER). |
-| [pyinstrument_cext][]             | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A simple C extension, without external dependencies |
 | [pybind11 cross build example][]  | ![github icon][] ![gitlab icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Same as pybind11 cmake_example but used to demo Linux ARM + Windows + macOS builds on GitLab |
 
 [scikit-learn]: https://github.com/scikit-learn/scikit-learn
@@ -138,6 +138,7 @@ title: Working examples
 [PyGame]: https://github.com/pygame/pygame
 [asyncpg]: https://github.com/MagicStack/asyncpg
 [cmake]: https://github.com/scikit-build/cmake-python-distributions
+[pyinstrument]: https://github.com/joerick/pyinstrument
 [scikit-image]: https://github.com/scikit-image/scikit-image
 [PyOxidizer]: https://github.com/indygreg/PyOxidizer
 [twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
@@ -208,14 +209,14 @@ title: Working examples
 [boost-histogram]: https://github.com/scikit-hep/boost-histogram
 [Python-WebRTC]: https://github.com/MarshalX/python-webrtc
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
-[pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
 [fathon]: https://github.com/stfbnc/fathon
+[pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
 [Arbor]: https://github.com/arbor-sim/arbor
 [clang-format]: https://github.com/ssciwr/clang-format-wheel
 [polaroid]: https://github.com/daggy1234/polaroid
-[cf-units]: https://github.com/SciTools/cf-units
 [etebase-py]: https://github.com/etesync/etebase-py
 [ninja]: https://github.com/scikit-build/ninja-python-distributions
+[cf-units]: https://github.com/SciTools/cf-units
 [numpythia]: https://github.com/scikit-hep/numpythia
 [pyjet]: https://github.com/scikit-hep/pyjet
 [ril]: https://github.com/Cryptex-github/ril-py
@@ -225,7 +226,6 @@ title: Working examples
 [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
 [xmlstarlet]: https://github.com/dimitern/xmlstarlet
 [werpy]: https://github.com/analyticsinmotion/werpy
-[pyinstrument_cext]: https://github.com/joerick/pyinstrument_cext
 [pybind11 cross build example]: https://github.com/wbarnha/pybind_cmake_example_crossbuild
 
 [github icon]: data/readme_icons/github.svg
diff --git a/noxfile.py b/noxfile.py
index b018a952d..ba3ce2ee8 100755
--- a/noxfile.py
+++ b/noxfile.py
@@ -103,7 +103,9 @@ def update_constraints(session: nox.Session) -> None:
         pyodide_build_version = pyodide["pyodide_build_version"]
         output_file = resources / f"constraints-pyodide{python_version.replace('.', '')}.txt"
         tmp_file = Path(session.create_tmp()) / "constraints-pyodide.in"
-        tmp_file.write_text(f"pip\nbuild[virtualenv]\npyodide-build=={pyodide_build_version}")
+        tmp_file.write_text(
+            f"pip\nbuild[virtualenv]\npyodide-build=={pyodide_build_version}\nclick<8.2"
+        )
         session.run(
             "uv",
             "pip",
diff --git a/test/test_build_frontend_args.py b/test/test_build_frontend_args.py
index b508728e4..bffdd14ff 100644
--- a/test/test_build_frontend_args.py
+++ b/test/test_build_frontend_args.py
@@ -22,6 +22,7 @@ def test_build_frontend_args(tmp_path, capfd, frontend_name):
     add_env = {"CIBW_BUILD_FRONTEND": f"{frontend_name}; args: -h"}
     if utils.platform == "pyodide":
         add_env["TERM"] = "dumb"  # disable color / style
+        add_env["NO_COLOR"] = "1"
     with pytest.raises(subprocess.CalledProcessError):
         utils.cibuildwheel_run(project_dir, add_env=add_env, single_python=True)
 
diff --git a/test/test_ssl.py b/test/test_ssl.py
index 5283a59d4..89ad4b80a 100644
--- a/test/test_ssl.py
+++ b/test/test_ssl.py
@@ -10,7 +10,7 @@
         from urllib.request import urlopen
 
         context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
-        data = urlopen("https://www.nist.gov", context=context)
+        data = urlopen("https://tls-v1-2.badssl.com", context=context)
         data = urlopen("https://raw.githubusercontent.com/pypa/cibuildwheel/main/CI.md", context=context)
         data = urlopen("https://raw.githubusercontent.com/pypa/cibuildwheel/main/CI.md")
         """

From cab1ff4076a47d93939d411708486a3062cc5731 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 14 May 2025 13:04:07 -0400
Subject: [PATCH 065/193] fix: support Python 3.14's color CLI (#2394)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .github/workflows/test.yml     | 2 ++
 bin/generate_schema.py         | 7 ++++++-
 bin/run_tests.py               | 6 +++++-
 bin/sample_build.py            | 6 +++++-
 cibuildwheel/__main__.py       | 6 +++++-
 pyproject.toml                 | 1 +
 test/test_projects/__main__.py | 8 ++++++--
 7 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d860ef40b..379edcdc0 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -46,6 +46,8 @@ jobs:
         include:
         - os: ubuntu-latest
           python_version: '3.11'
+        - os: ubuntu-latest
+          python_version: '3.14'
     timeout-minutes: 180
     steps:
     - uses: actions/checkout@v4
diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index 728a5a56c..e5758fd77 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -6,12 +6,17 @@
 
 import argparse
 import copy
+import functools
 import json
+import sys
 from typing import Any
 
 import yaml
 
-parser = argparse.ArgumentParser()
+make_parser = functools.partial(argparse.ArgumentParser, allow_abbrev=False)
+if sys.version_info >= (3, 14):
+    make_parser = functools.partial(make_parser, color=True, suggest_on_error=True)
+parser = make_parser()
 parser.add_argument("--schemastore", action="store_true", help="Generate schema_store version")
 args = parser.parse_args()
 
diff --git a/bin/run_tests.py b/bin/run_tests.py
index b807938b1..1c0923948 100755
--- a/bin/run_tests.py
+++ b/bin/run_tests.py
@@ -2,6 +2,7 @@
 
 
 import argparse
+import functools
 import os
 import subprocess
 import sys
@@ -13,7 +14,10 @@
     else:
         default_cpu_count = os.process_cpu_count() or 2
 
-    parser = argparse.ArgumentParser()
+    make_parser = functools.partial(argparse.ArgumentParser, allow_abbrev=False)
+    if sys.version_info >= (3, 14):
+        make_parser = functools.partial(make_parser, color=True, suggest_on_error=True)
+    parser = make_parser()
     parser.add_argument(
         "--run-podman", action="store_true", default=False, help="run podman tests (linux only)"
     )
diff --git a/bin/sample_build.py b/bin/sample_build.py
index 41371da10..d98ce28ed 100755
--- a/bin/sample_build.py
+++ b/bin/sample_build.py
@@ -2,6 +2,7 @@
 
 
 import argparse
+import functools
 import os
 import subprocess
 import sys
@@ -12,7 +13,10 @@
     # move cwd to the project root
     os.chdir(Path(__file__).resolve().parents[1])
 
-    parser = argparse.ArgumentParser(description="Runs a sample build")
+    make_parser = functools.partial(argparse.ArgumentParser, allow_abbrev=False)
+    if sys.version_info >= (3, 14):
+        make_parser = functools.partial(make_parser, color=True, suggest_on_error=True)
+    parser = make_parser(description="Runs a sample build")
     parser.add_argument("project_python_path", nargs="?", default="test.test_0_basic.basic_project")
 
     options = parser.parse_args()
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index eded443de..934d80c4e 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -1,6 +1,7 @@
 import argparse
 import contextlib
 import dataclasses
+import functools
 import os
 import shutil
 import sys
@@ -79,7 +80,10 @@ def main_inner(global_options: GlobalOptions) -> None:
     rather than exiting directly.
     """
 
-    parser = argparse.ArgumentParser(
+    make_parser = functools.partial(argparse.ArgumentParser, allow_abbrev=False)
+    if sys.version_info >= (3, 14):
+        make_parser = functools.partial(make_parser, color=True, suggest_on_error=True)
+    parser = make_parser(
         description="Build wheels for all the platforms.",
         epilog="""
             Most options are supplied via environment variables or in
diff --git a/pyproject.toml b/pyproject.toml
index 1495b9b32..18585c549 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -32,6 +32,7 @@ classifiers = [
     "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: 3.12",
     "Programming Language :: Python :: 3.13",
+    "Programming Language :: Python :: 3.14",
     "Programming Language :: Python :: Implementation :: CPython",
     "Topic :: Software Development :: Build Tools",
 ]
diff --git a/test/test_projects/__main__.py b/test/test_projects/__main__.py
index c7c725831..2809b17b1 100644
--- a/test/test_projects/__main__.py
+++ b/test/test_projects/__main__.py
@@ -1,13 +1,17 @@
+import argparse
+import functools
 import importlib
 import subprocess
 import sys
 import tempfile
-from argparse import ArgumentParser
 from pathlib import Path
 
 
 def main() -> None:
-    parser = ArgumentParser(
+    make_parser = functools.partial(argparse.ArgumentParser, allow_abbrev=False)
+    if sys.version_info >= (3, 14):
+        make_parser = functools.partial(make_parser, color=True, suggest_on_error=True)
+    parser = make_parser(
         prog="python -m test.test_projects", description="Generate a test project to check it out"
     )
     parser.add_argument(

From c2cffa3d1eb3059af30053a376657dabd8607e1a Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 15 May 2025 09:06:48 -0400
Subject: [PATCH 066/193] feat: adding pypy-eol enable group (#2393)

---
 bin/generate_schema.py                        |  1 +
 .../resources/cibuildwheel.schema.json        |  1 +
 cibuildwheel/selector.py                      |  5 +-
 docs/options.md                               |  1 +
 test/utils.py                                 |  5 +-
 unit_test/build_selector_test.py              | 69 ++++++++++++++-----
 unit_test/option_prepare_test.py              |  2 +-
 7 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index e5758fd77..ac97f8e9b 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -36,6 +36,7 @@
       - cpython-freethreading
       - cpython-prerelease
       - pypy
+      - pypy-eol
       - cpython-experimental-riscv64
   description: A Python version or flavor to enable.
 additionalProperties: false
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 46ad0020f..8ffa0b75e 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -16,6 +16,7 @@
         "cpython-freethreading",
         "cpython-prerelease",
         "pypy",
+        "pypy-eol",
         "cpython-experimental-riscv64"
       ]
     },
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 2d78aea0e..c3f9dea4b 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -32,6 +32,7 @@ class EnableGroup(StrEnum):
     CPythonFreeThreading = "cpython-freethreading"
     CPythonPrerelease = "cpython-prerelease"
     PyPy = "pypy"
+    PyPyEoL = "pypy-eol"
     CPythonExperimentalRiscV64 = "cpython-experimental-riscv64"
     GraalPy = "graalpy"
 
@@ -87,7 +88,9 @@ def __call__(self, build_id: str) -> bool:
             return False
         if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp314*"):
             return False
-        if EnableGroup.PyPy not in self.enable and fnmatch(build_id, "pp*"):
+        if EnableGroup.PyPy not in self.enable and fnmatch(build_id, "pp31*"):
+            return False
+        if EnableGroup.PyPyEoL not in self.enable and fnmatch(build_id, "pp3?-*"):
             return False
         if EnableGroup.CPythonExperimentalRiscV64 not in self.enable and fnmatch(
             build_id, "*_riscv64"
diff --git a/docs/options.md b/docs/options.md
index 1a79d3750..2b8113970 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -321,6 +321,7 @@ values are:
   The build identifiers for those variants have a `t` suffix in their
   `python_tag` (e.g. `cp313t-manylinux_x86_64`).
 - `pypy`: Enable PyPy.
+- `pypy-eol`: Enable PyPy versions that have passed end of life (if still available).
 - `cpython-experimental-riscv64`: Enable experimental riscv64 builds. Those builds
   are disabled by default as they can't be uploaded to PyPI and a PEP will most likely
   be required before this can happen.
diff --git a/test/utils.py b/test/utils.py
index 7e566f503..1ed93642c 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -254,10 +254,13 @@ def _expected_wheels(
             if EnableGroup.CPythonFreeThreading in enable_groups:
                 python_abi_tags.append("cp314-cp314t")
 
-        if EnableGroup.PyPy in enable_groups:
+        if EnableGroup.PyPyEoL in enable_groups:
             python_abi_tags += [
                 "pp38-pypy38_pp73",
                 "pp39-pypy39_pp73",
+            ]
+        if EnableGroup.PyPy in enable_groups:
+            python_abi_tags += [
                 "pp310-pypy310_pp73",
                 "pp311-pypy311_pp73",
             ]
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index ea72bf7c2..6da657fca 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -14,8 +14,8 @@ def test_build():
     assert build_selector("cp311-manylinux_x86_64")
     assert build_selector("cp312-manylinux_x86_64")
     assert build_selector("cp313-manylinux_x86_64")
-    assert build_selector("pp36-manylinux_x86_64")
-    assert build_selector("pp37-manylinux_x86_64")
+    assert build_selector("pp310-manylinux_x86_64")
+    assert build_selector("pp311-manylinux_x86_64")
     assert build_selector("cp36-manylinux_i686")
     assert build_selector("cp37-manylinux_i686")
     assert build_selector("cp36-macosx_intel")
@@ -23,20 +23,20 @@ def test_build():
     assert build_selector("cp39-macosx_intel")
     assert build_selector("cp39-macosx_universal2")
     assert build_selector("cp39-macosx_arm64")
-    assert not build_selector("pp36-macosx_intel")
-    assert not build_selector("pp37-macosx_intel")
+    assert not build_selector("pp310-macosx_intel")
+    assert not build_selector("pp311-macosx_intel")
     assert build_selector("cp36-win32")
     assert build_selector("cp37-win32")
-    assert not build_selector("pp36-win32")
-    assert not build_selector("pp37-win32")
+    assert not build_selector("pp310-win32")
+    assert not build_selector("pp311-win32")
     assert build_selector("cp36-win_amd64")
     assert build_selector("cp37-win_amd64")
     assert build_selector("cp310-win_amd64")
     assert build_selector("cp311-win_amd64")
     assert build_selector("cp312-win_amd64")
     assert build_selector("cp313-win_amd64")
-    assert not build_selector("pp36-win_amd64")
-    assert not build_selector("pp37-win_amd64")
+    assert not build_selector("pp310-win_amd64")
+    assert not build_selector("pp311-win_amd64")
 
 
 def test_build_filter_pre():
@@ -53,24 +53,59 @@ def test_build_filter_pre():
     assert not build_selector("cp313t-manylinux_x86_64")
 
 
-def test_skip():
+def test_build_filter_pypy():
     build_selector = BuildSelector(
         build_config="*",
-        skip_config="pp36-* cp3?-manylinux_i686 cp36-win* *-win32",
+        skip_config="",
         enable=frozenset([EnableGroup.PyPy]),
     )
+    assert build_selector("pp310-manylinux_x86_64")
+    assert build_selector("pp311-manylinux_x86_64")
+    assert not build_selector("pp38-manylinux_x86_64")
+    assert not build_selector("pp39-manylinux_x86_64")
 
-    assert not build_selector("pp36-manylinux_x86_64")
-    assert build_selector("pp37-manylinux_x86_64")
+
+def test_build_filter_pypy_eol():
+    build_selector = BuildSelector(
+        build_config="*",
+        skip_config="",
+        enable=frozenset([EnableGroup.PyPyEoL]),
+    )
+    assert not build_selector("pp310-manylinux_x86_64")
+    assert not build_selector("pp311-manylinux_x86_64")
+    assert build_selector("pp38-manylinux_x86_64")
+    assert build_selector("pp39-manylinux_x86_64")
+
+
+def test_build_filter_pypy_all():
+    build_selector = BuildSelector(
+        build_config="*",
+        skip_config="",
+        enable=frozenset([EnableGroup.PyPyEoL, EnableGroup.PyPy]),
+    )
+    assert build_selector("pp310-manylinux_x86_64")
+    assert build_selector("pp311-manylinux_x86_64")
     assert build_selector("pp38-manylinux_x86_64")
-    assert build_selector("pp37-manylinux_i686")
-    assert build_selector("pp38-manylinux_i686")
+    assert build_selector("pp39-manylinux_x86_64")
+
+
+def test_skip():
+    build_selector = BuildSelector(
+        build_config="*",
+        skip_config="pp310-* cp3?-manylinux_i686 cp36-win* *-win32",
+        enable=frozenset([EnableGroup.PyPy]),
+    )
+
+    assert not build_selector("pp310-manylinux_x86_64")
+    assert build_selector("pp311-manylinux_x86_64")
+    assert not build_selector("pp37-manylinux_i686")
+    assert not build_selector("pp38-manylinux_i686")
     assert build_selector("cp36-manylinux_x86_64")
     assert build_selector("cp37-manylinux_x86_64")
     assert not build_selector("cp36-manylinux_i686")
     assert not build_selector("cp37-manylinux_i686")
-    assert not build_selector("pp36-macosx_10_6_intel")
-    assert build_selector("pp37-macosx_10_6_intel")
+    assert not build_selector("pp39-macosx_10_6_intel")
+    assert build_selector("pp311-macosx_10_6_intel")
     assert build_selector("cp36-macosx_10_6_intel")
     assert build_selector("cp37-macosx_10_6_intel")
     assert not build_selector("cp36-win32")
@@ -117,7 +152,7 @@ def test_build_limited_python():
         build_config="*",
         skip_config="",
         requires_python=SpecifierSet(">=3.7"),
-        enable=frozenset([EnableGroup.PyPy]),
+        enable=frozenset([EnableGroup.PyPy, EnableGroup.PyPyEoL]),
     )
 
     assert not build_selector("cp36-manylinux_x86_64")
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 09ff57525..2f87044ad 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -104,7 +104,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 [tool.cibuildwheel]
 manylinux-x86_64-image = "manylinux_2_28"
 musllinux-x86_64-image = "musllinux_1_2"
-enable = ["pypy", "graalpy", "cpython-freethreading"]
+enable = ["pypy", "pypy-eol", "graalpy", "cpython-freethreading"]
 
 # Before Python 3.10, use manylinux2014
 [[tool.cibuildwheel.overrides]]

From 166465b56d3a317a12fae0c7eed538fcdd480a43 Mon Sep 17 00:00:00 2001
From: Jinzhe Zeng <jinzhe.zeng@ustc.edu.cn>
Date: Fri, 16 May 2025 13:41:19 +0800
Subject: [PATCH 067/193] doc: update Windows ARM for GH Actions (#2396)

It's no more cross compilation and can run tests.
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 21049c093..bb046a070 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ Usage
 
 |                 | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS |
 |-----------------|-------|-------|---------|-----------|-----------|-------------|-----|
-| GitHub Actions  | ✅    | ✅    | ✅       | ✅        | ✅        | ✅²         | ✅³  |
+| GitHub Actions  | ✅    | ✅    | ✅       | ✅        | ✅        | ✅          | ✅³  |
 | Azure Pipelines | ✅    | ✅    | ✅       |           | ✅        | ✅²         | ✅³  |
 | Travis CI       | ✅    |       | ✅      | ✅        |           |             |     |
 | CircleCI        | ✅    | ✅    |         | ✅        | ✅        |             | ✅³  |

From 1bc7e904b8fcefa45e255b671f0304d211314a05 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Fri, 16 May 2025 11:11:16 +0100
Subject: [PATCH 068/193] Improve integration test options (#2385)

* Improve integration test options

- Keep the platform and enable options in os.environ, don't duplicate state in utils
- Provide more user-friendly options for setting enable and platform in integration tests

* Ensure that CIBW_ENABLE is set in the test process

* Use get_enable_groups() where necessary
---
 .github/workflows/test.yml       |  2 +-
 docs/contributing.md             |  4 +--
 test/conftest.py                 | 56 +++++++++++++++++++++++++++-----
 test/test_0_basic.py             |  3 +-
 test/test_abi_variants.py        |  4 +--
 test/test_before_build.py        |  4 +--
 test/test_before_test.py         |  2 +-
 test/test_build_frontend_args.py |  4 +--
 test/test_container_engine.py    |  4 +--
 test/test_container_images.py    |  2 +-
 test/test_cpp_standards.py       |  8 ++---
 test/test_dependency_versions.py | 16 +++++----
 test/test_emulation.py           |  2 +-
 test/test_environment.py         |  4 +--
 test/test_ios.py                 | 10 +++---
 test/test_linux_python.py        |  2 +-
 test/test_macos_archs.py         | 12 +++----
 test/test_manylinuxXXXX_only.py  |  2 +-
 test/test_musllinux_X_Y_only.py  |  2 +-
 test/test_pep518.py              |  4 +--
 test/test_troubleshooting.py     |  4 +--
 test/test_wheel_tag.py           |  2 +-
 test/test_windows.py             |  2 +-
 test/utils.py                    | 50 ++++++++++++++++++----------
 24 files changed, 130 insertions(+), 75 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 379edcdc0..92d2d659b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -83,7 +83,7 @@ jobs:
           CIBW_ENABLE=all
         else
           # get the default CIBW_ENABLE value from the test module
-          CIBW_ENABLE=$(uv run --no-sync python -c 'import sys, test.conftest as c; sys.stdout.write(c.DEFAULT_CIBW_ENABLE)')
+          CIBW_ENABLE=$(uv run --no-sync python -c 'import sys, test.utils as c; sys.stdout.write(c.DEFAULT_CIBW_ENABLE)')
 
           # if this is a PR, check for labels
           if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then
diff --git a/docs/contributing.md b/docs/contributing.md
index 86cd49bdf..40ebaa8b6 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -78,11 +78,11 @@ nox -s tests -- test -k before_build
 
 A few notes-
 
-- Because they run inside a container, Linux tests can run on all platforms where Docker is installed, so they're convenient for running integration tests locally. Set CIBW_PLATFORM to do this: `CIBW_PLATFORM=linux nox -s tests -- test`.
+- Because they run inside a container, Linux tests can run on all platforms where Docker is installed, so they're convenient for running integration tests locally. Set the `--platform` flag on pytest to do this: `nox -s tests -- test --platform linux`.
 
 - Running the macOS integration tests requires _system installs_ of Python from python.org for all the versions that are tested. We won't attempt to install these when running locally, but you can do so manually using the URL in the error message that is printed when the install is not found.
 
-- The 'enable groups' run by default are just 'cpython-prerelease' and 'cpython-freethreading'. You can add other groups like pypy or graalpy by setting the [CIBW_ENABLE](options.md#enable) environment variable. On GitHub PRs, you can add a label to the PR to enable these groups.
+- The ['enable groups'](options.md#enable) run by default are just 'cpython-prerelease' and 'cpython-freethreading'. You can add other groups like pypy or graalpy by passing the `--enable` argument to pytest, i.e. `nox -s tests -- test --enable pypy`. On GitHub PRs, you can add a label to the PR to enable these groups.
 
 #### Running pytest directly
 
diff --git a/test/conftest.py b/test/conftest.py
index 8c3e15221..88d6175b6 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -9,12 +9,11 @@
 from cibuildwheel.architecture import Architecture
 from cibuildwheel.ci import detect_ci_provider
 from cibuildwheel.options import CommandLineArguments, Options
+from cibuildwheel.selector import EnableGroup
+from cibuildwheel.typing import PLATFORMS
 from cibuildwheel.venv import find_uv
 
-from .utils import EMULATED_ARCHS, platform
-
-# default to just cpython
-DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease cpython-experimental-riscv64"
+from .utils import DEFAULT_CIBW_ENABLE, EMULATED_ARCHS, get_platform
 
 
 def pytest_addoption(parser: pytest.Parser) -> None:
@@ -32,8 +31,47 @@ def pytest_addoption(parser: pytest.Parser) -> None:
         default=False,
         help="macOS cp38 uses the universal2 installer",
     )
+    parser.addoption(
+        "--enable",
+        action="store",
+        default=None,
+        help="Set the CIBW_ENABLE environment variable for all tests.",
+    )
+    parser.addoption(
+        "--platform",
+        action="store",
+        default=None,
+        help="Set the CIBW_PLATFORM environment variable for all tests.",
+    )
+
+
+def pytest_configure(config):
+    flag_enable = config.getoption("--enable")
+    flag_platform = config.getoption("--platform")
+
+    if flag_enable is not None and "CIBW_ENABLE" in os.environ:
+        msg = (
+            "Both --enable pytest option and CIBW_ENABLE environment variable are set. "
+            "Please specify only one."
+        )
+        raise pytest.UsageError(msg)
+    if flag_platform is not None and "CIBW_PLATFORM" in os.environ:
+        msg = (
+            "Both --platform pytest option and CIBW_PLATFORM environment variable are set. "
+            "Please specify only one."
+        )
+        raise pytest.UsageError(msg)
+
+    if flag_enable is not None:
+        EnableGroup.parse_option_value(flag_enable)
+        os.environ["CIBW_ENABLE"] = flag_enable
+    if flag_enable is None and "CIBW_ENABLE" not in os.environ:
+        # Set default value for CIBW_ENABLE
+        os.environ["CIBW_ENABLE"] = DEFAULT_CIBW_ENABLE
 
-    os.environ.setdefault("CIBW_ENABLE", DEFAULT_CIBW_ENABLE)
+    if flag_platform is not None:
+        assert flag_platform in PLATFORMS, f"Invalid platform: {flag_platform}"
+        os.environ["CIBW_PLATFORM"] = flag_platform
 
 
 def docker_warmup(request: pytest.FixtureRequest) -> None:
@@ -91,7 +129,7 @@ def docker_warmup_fixture(
     request: pytest.FixtureRequest, tmp_path_factory: pytest.TempPathFactory, worker_id: str
 ) -> None:
     # if we're in CI testing linux, let's warm-up docker images
-    if detect_ci_provider() is None or platform != "linux":
+    if detect_ci_provider() is None or get_platform() != "linux":
         return None
     if request.config.getoption("--run-emulation", default=None) is not None:
         # emulation tests only run one test in CI, caching the image only slows down the test
@@ -116,7 +154,7 @@ def docker_warmup_fixture(
 @pytest.fixture(params=["pip", "build"])
 def build_frontend_env_nouv(request: pytest.FixtureRequest) -> dict[str, str]:
     frontend = request.param
-    if platform == "pyodide" and frontend == "pip":
+    if get_platform() == "pyodide" and frontend == "pip":
         pytest.skip("Can't use pip as build frontend for pyodide platform")
 
     return {"CIBW_BUILD_FRONTEND": frontend}
@@ -125,7 +163,7 @@ def build_frontend_env_nouv(request: pytest.FixtureRequest) -> dict[str, str]:
 @pytest.fixture
 def build_frontend_env(build_frontend_env_nouv: dict[str, str]) -> dict[str, str]:
     frontend = build_frontend_env_nouv["CIBW_BUILD_FRONTEND"]
-    if frontend != "build" or platform == "pyodide" or find_uv() is None:
+    if frontend != "build" or get_platform() == "pyodide" or find_uv() is None:
         return build_frontend_env_nouv
 
     return {"CIBW_BUILD_FRONTEND": "build[uv]"}
@@ -134,7 +172,7 @@ def build_frontend_env(build_frontend_env_nouv: dict[str, str]) -> dict[str, str
 @pytest.fixture
 def docker_cleanup() -> Generator[None, None, None]:
     def get_images() -> set[str]:
-        if detect_ci_provider() is None or platform != "linux":
+        if detect_ci_provider() is None or get_platform() != "linux":
             return set()
         images = subprocess.run(
             ["docker", "image", "ls", "--format", "{{json .ID}}"],
diff --git a/test/test_0_basic.py b/test/test_0_basic.py
index d0503c105..c1a8dfcbf 100644
--- a/test/test_0_basic.py
+++ b/test/test_0_basic.py
@@ -1,4 +1,3 @@
-import os
 import textwrap
 
 import pytest
@@ -40,7 +39,7 @@ def test(tmp_path, build_frontend_env, capfd):
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
     assert set(actual_wheels) == set(expected_wheels)
 
-    enable_groups = EnableGroup.parse_option_value(os.environ.get("CIBW_ENABLE", ""))
+    enable_groups = utils.get_enable_groups()
     if EnableGroup.GraalPy not in enable_groups:
         # Verify pip warning not shown
         captured = capfd.readouterr()
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
index 62f03850f..780f6d908 100644
--- a/test/test_abi_variants.py
+++ b/test/test_abi_variants.py
@@ -47,7 +47,7 @@ def test_abi3(tmp_path):
     )
 
     # check that the expected wheels are produced
-    if utils.platform == "pyodide":
+    if utils.get_platform() == "pyodide":
         # there's only 1 possible configuration for pyodide, cp312
         expected_wheels = utils.expected_wheels("spam", "0.1.0", python_abi_tags=["cp310-abi3"])
     else:
@@ -196,7 +196,7 @@ def test_abi_none(tmp_path, capfd):
     # check that each wheel was built once, and reused
     captured = capfd.readouterr()
     assert "Building wheel..." in captured.out
-    if utils.platform == "pyodide":
+    if utils.get_platform() == "pyodide":
         # there's only 1 possible configuration for pyodide, we won't see the message expected on following builds
         assert "Found previously built wheel" not in captured.out
     else:
diff --git a/test/test_before_build.py b/test/test_before_build.py
index 9494de7bd..767bb06cc 100644
--- a/test/test_before_build.py
+++ b/test/test_before_build.py
@@ -6,7 +6,7 @@
 from . import test_projects, utils
 
 # pyodide does not support building without isolation, need to check the base_prefix
-SYS_PREFIX = f"sys.{'base_' if utils.platform == 'pyodide' else ''}prefix"
+SYS_PREFIX = f"sys.{'base_' if utils.get_platform() == 'pyodide' else ''}prefix"
 
 
 project_with_before_build_asserts = test_projects.new_c_project(
@@ -45,7 +45,7 @@ def test(tmp_path):
         f'''python -c "import pathlib, sys; pathlib.Path('{{project}}/pythonprefix_bb.txt').write_text({SYS_PREFIX})"'''
     )
     frontend = "build"
-    if utils.platform != "pyodide":
+    if utils.get_platform() != "pyodide":
         before_build = f"python -m pip install setuptools && {before_build}"
         frontend = f"{frontend};args: --no-isolation"
 
diff --git a/test/test_before_test.py b/test/test_before_test.py
index a3700f843..c6ed5a7bb 100644
--- a/test/test_before_test.py
+++ b/test/test_before_test.py
@@ -43,7 +43,7 @@ def test(tmp_path, build_frontend_env):
         '''python -c "import pathlib, sys; pathlib.Path('{project}/pythonprefix_bt.txt').write_text(sys.prefix)"''',
     ]
 
-    if utils.platform == "pyodide":
+    if utils.get_platform() == "pyodide":
         before_test_steps.extend(
             ["pyodide build {project}/dependency", "pip install --find-links dist/ spam"]
         )
diff --git a/test/test_build_frontend_args.py b/test/test_build_frontend_args.py
index bffdd14ff..83c0a1775 100644
--- a/test/test_build_frontend_args.py
+++ b/test/test_build_frontend_args.py
@@ -20,7 +20,7 @@ def test_build_frontend_args(tmp_path, capfd, frontend_name):
 
     # the build will fail because the frontend is called with '-h' - it prints the help message
     add_env = {"CIBW_BUILD_FRONTEND": f"{frontend_name}; args: -h"}
-    if utils.platform == "pyodide":
+    if utils.get_platform() == "pyodide":
         add_env["TERM"] = "dumb"  # disable color / style
         add_env["NO_COLOR"] = "1"
     with pytest.raises(subprocess.CalledProcessError):
@@ -33,7 +33,7 @@ def test_build_frontend_args(tmp_path, capfd, frontend_name):
     if frontend_name == "pip":
         assert "Usage:" in captured.out
         assert "Wheel Options:" in captured.out
-    elif utils.platform == "pyodide":
+    elif utils.get_platform() == "pyodide":
         assert "Usage: pyodide build" in captured.out
     else:
         assert "usage:" in captured.out
diff --git a/test/test_container_engine.py b/test/test_container_engine.py
index 4503b3b4e..510916f06 100644
--- a/test/test_container_engine.py
+++ b/test/test_container_engine.py
@@ -6,7 +6,7 @@
 
 
 def test_podman(tmp_path, capfd, request):
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("the test is only relevant to the linux build")
 
     if not request.config.getoption("--run-podman"):
@@ -36,7 +36,7 @@ def test_podman(tmp_path, capfd, request):
 
 
 def test_create_args(tmp_path, capfd):
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("the test is only relevant to the linux build")
 
     project_dir = tmp_path / "project"
diff --git a/test/test_container_images.py b/test/test_container_images.py
index f1d4fda8b..02263c649 100644
--- a/test/test_container_images.py
+++ b/test/test_container_images.py
@@ -23,7 +23,7 @@
 
 @pytest.mark.usefixtures("docker_cleanup")
 def test(tmp_path):
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("the test is only relevant to the linux build")
     if platform.machine() not in ["x86_64", "i686"]:
         pytest.skip(
diff --git a/test/test_cpp_standards.py b/test/test_cpp_standards.py
index 85fcb6d96..fb7a0a84e 100644
--- a/test/test_cpp_standards.py
+++ b/test/test_cpp_standards.py
@@ -57,7 +57,7 @@ def test_cpp11(tmp_path):
     project_dir = tmp_path / "project"
     cpp11_project = cpp_test_project.copy()
     cpp11_project.template_context["extra_compile_args"] = (
-        ["/std:c++11"] if utils.platform == "windows" else ["-std=c++11"]
+        ["/std:c++11"] if utils.get_platform() == "windows" else ["-std=c++11"]
     )
     cpp11_project.template_context["spam_cpp_top_level_add"] = "#include <array>"
     cpp11_project.generate(project_dir)
@@ -73,7 +73,7 @@ def test_cpp14(tmp_path):
     project_dir = tmp_path / "project"
     cpp14_project = cpp_test_project.copy()
     cpp14_project.template_context["extra_compile_args"] = (
-        ["/std:c++14"] if utils.platform == "windows" else ["-std=c++14"]
+        ["/std:c++14"] if utils.get_platform() == "windows" else ["-std=c++14"]
     )
     cpp14_project.template_context["spam_cpp_top_level_add"] = "int a = 100'000;"
     cpp14_project.generate(project_dir)
@@ -89,7 +89,7 @@ def test_cpp17(tmp_path):
     project_dir = tmp_path / "project"
     cpp17_project = cpp_test_project.copy()
     cpp17_project.template_context["extra_compile_args"] = [
-        "/std:c++17" if utils.platform == "windows" else "-std=c++17"
+        "/std:c++17" if utils.get_platform() == "windows" else "-std=c++17"
     ]
     cpp17_project.template_context["spam_cpp_top_level_add"] = r"""
     #include <utility>
@@ -98,7 +98,7 @@ def test_cpp17(tmp_path):
     cpp17_project.generate(project_dir)
 
     add_env = {}
-    if utils.platform == "macos":
+    if utils.get_platform() == "macos":
         add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.13"
 
     actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env, single_python=True)
diff --git a/test/test_dependency_versions.py b/test/test_dependency_versions.py
index d4b9416f3..6e29adef1 100644
--- a/test/test_dependency_versions.py
+++ b/test/test_dependency_versions.py
@@ -53,11 +53,15 @@ def get_versions_from_constraint_file(constraint_file: Path) -> dict[str, str]:
 
 @pytest.mark.parametrize("python_version", ["3.8", "3.12"])
 def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv):
-    if utils.platform == "linux":
+    if utils.get_platform() == "linux":
         pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
-    if python_version != "3.12" and utils.platform == "pyodide":
+    if python_version != "3.12" and utils.get_platform() == "pyodide":
         pytest.skip(f"pyodide does not support Python {python_version}")
-    if python_version == "3.8" and utils.platform == "windows" and platform.machine() == "ARM64":
+    if (
+        python_version == "3.8"
+        and utils.get_platform() == "windows"
+        and platform.machine() == "ARM64"
+    ):
         pytest.skip(f"Windows ARM64 does not support Python {python_version}")
 
     project_dir = tmp_path / "project"
@@ -96,7 +100,7 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv):
 
 @pytest.mark.parametrize("method", ["inline", "file"])
 def test_dependency_constraints(method, tmp_path, build_frontend_env_nouv):
-    if utils.platform == "linux":
+    if utils.get_platform() == "linux":
         pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
 
     project_dir = tmp_path / "project"
@@ -129,7 +133,7 @@ def test_dependency_constraints(method, tmp_path, build_frontend_env_nouv):
     build_environment = {}
 
     if (
-        utils.platform == "windows"
+        utils.get_platform() == "windows"
         and method == "file"
         and build_frontend_env_nouv["CIBW_BUILD_FRONTEND"] == "build"
     ):
@@ -159,7 +163,7 @@ def test_dependency_constraints(method, tmp_path, build_frontend_env_nouv):
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
 
     if (
-        utils.platform == "windows"
+        utils.get_platform() == "windows"
         and method == "file"
         and build_frontend_env_nouv["CIBW_BUILD_FRONTEND"] == "build"
     ):
diff --git a/test/test_emulation.py b/test/test_emulation.py
index 9ffdc6de8..9749d9da0 100644
--- a/test/test_emulation.py
+++ b/test/test_emulation.py
@@ -53,7 +53,7 @@ def test(tmp_path, request):
 
 
 def test_setting_arch_on_other_platforms(tmp_path, capfd):
-    if utils.platform == "linux":
+    if utils.get_platform() == "linux":
         pytest.skip("this test checks the behaviour on platforms other than linux")
 
     project_dir = tmp_path / "project"
diff --git a/test/test_environment.py b/test/test_environment.py
index 6e7ed6fd8..ee50155b5 100644
--- a/test/test_environment.py
+++ b/test/test_environment.py
@@ -64,7 +64,7 @@ def test_overridden_path(tmp_path, capfd):
     output_dir.mkdir()
 
     # mess up PATH, somehow
-    if utils.platform == "linux":
+    if utils.get_platform() == "linux":
         with pytest.raises(subprocess.CalledProcessError):
             utils.cibuildwheel_run(
                 project_dir,
@@ -128,7 +128,7 @@ def test_overridden_pip_constraint(tmp_path, build_frontend):
     )
     project.generate(project_dir)
 
-    if utils.platform == "linux":
+    if utils.get_platform() == "linux":
         # put the constraints file in the project directory, so it's available
         # in the docker container
         constraints_file = project_dir / "constraints.txt"
diff --git a/test/test_ios.py b/test/test_ios.py
index c3cfa61af..7c5080da5 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -38,7 +38,7 @@ def test_platform(self):
     ],
 )
 def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
         pytest.skip("this test only works with Xcode 13.0 or greater")
@@ -103,7 +103,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
 
 def test_no_test_sources(tmp_path, capfd):
     """Build will fail if test-sources isn't defined."""
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
         pytest.skip("this test only works with Xcode 13.0 or greater")
@@ -130,7 +130,7 @@ def test_no_test_sources(tmp_path, capfd):
 
 def test_missing_xbuild_tool(tmp_path, capfd):
     """Build will fail if xbuild-tools references a non-existent tool."""
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
         pytest.skip("this test only works with Xcode 13.0 or greater")
@@ -158,7 +158,7 @@ def test_missing_xbuild_tool(tmp_path, capfd):
 
 def test_no_xbuild_tool_definition(tmp_path, capfd):
     """Build will succeed with a warning if there is no xbuild-tools definition."""
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
         pytest.skip("this test only works with Xcode 13.0 or greater")
@@ -203,7 +203,7 @@ def test_no_xbuild_tool_definition(tmp_path, capfd):
 
 def test_empty_xbuild_tool_definition(tmp_path, capfd):
     """Build will succeed with no warning if there is an empty xbuild-tools definition."""
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
         pytest.skip("this test only works with Xcode 13.0 or greater")
diff --git a/test/test_linux_python.py b/test/test_linux_python.py
index 7c5f58a56..48a70c2e1 100644
--- a/test/test_linux_python.py
+++ b/test/test_linux_python.py
@@ -7,7 +7,7 @@
 
 
 def test_python_exist(tmp_path, capfd):
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("the test is only relevant to the linux build")
     machine = platform.machine()
     if machine not in ["x86_64", "i686"]:
diff --git a/test/test_macos_archs.py b/test/test_macos_archs.py
index 848303ec8..f38cff567 100644
--- a/test/test_macos_archs.py
+++ b/test/test_macos_archs.py
@@ -20,7 +20,7 @@
 
 
 def test_cross_compiled_build(tmp_path):
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test is only relevant to macos")
     if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
@@ -54,7 +54,7 @@ def test_cross_compiled_build(tmp_path):
     ],
 )
 def test_cross_compiled_test(tmp_path, capfd, build_universal2, test_config):
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test is only relevant to macos")
     if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
@@ -114,7 +114,7 @@ def test_cross_compiled_test(tmp_path, capfd, build_universal2, test_config):
 
 def test_deployment_target_warning_is_firing(tmp_path, capfd):
     # force the warning to check that we can detect it if it happens
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test is only relevant to macos")
 
     project_dir = tmp_path / "project"
@@ -136,7 +136,7 @@ def test_deployment_target_warning_is_firing(tmp_path, capfd):
 
 @pytest.mark.parametrize("skip_arm64_test", [False, True])
 def test_universal2_testing_on_x86_64(tmp_path, capfd, skip_arm64_test):
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test is only relevant to macos")
     if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
@@ -176,7 +176,7 @@ def test_universal2_testing_on_x86_64(tmp_path, capfd, skip_arm64_test):
 
 def test_universal2_testing_on_arm64(build_frontend_env, tmp_path, capfd):
     # cibuildwheel should test the universal2 wheel on both x86_64 and arm64, when run on arm64
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test is only relevant to macos")
     if platform.machine() != "arm64":
         pytest.skip("this test only works on arm64")
@@ -206,7 +206,7 @@ def test_universal2_testing_on_arm64(build_frontend_env, tmp_path, capfd):
 
 
 def test_cp38_arm64_testing(tmp_path, capfd, request):
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("this test is only relevant to macos")
     if utils.get_xcode_version() < (12, 2):
         pytest.skip("this test only works with Xcode 12.2 or greater")
diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py
index c8d2c6d99..5f0839711 100644
--- a/test/test_manylinuxXXXX_only.py
+++ b/test/test_manylinuxXXXX_only.py
@@ -62,7 +62,7 @@
 )
 @pytest.mark.usefixtures("docker_cleanup")
 def test(manylinux_image, tmp_path):
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("the container image test is only relevant to the linux build")
     elif manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "i686":
         pytest.skip(f"{manylinux_image} doesn't exist for i686 architecture")
diff --git a/test/test_musllinux_X_Y_only.py b/test/test_musllinux_X_Y_only.py
index f8396aa6a..5e2f2e246 100644
--- a/test/test_musllinux_X_Y_only.py
+++ b/test/test_musllinux_X_Y_only.py
@@ -28,7 +28,7 @@
 )
 @pytest.mark.usefixtures("docker_cleanup")
 def test(musllinux_image, tmp_path):
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("the container image test is only relevant to the linux build")
 
     project_dir = tmp_path / "project"
diff --git a/test/test_pep518.py b/test/test_pep518.py
index c861f0da3..d519cc297 100644
--- a/test/test_pep518.py
+++ b/test/test_pep518.py
@@ -37,7 +37,7 @@ def test_pep518(tmp_path, build_frontend_env):
     # from a virtualenv seeded executable. See
     # https://github.com/oracle/graalpython/issues/491 and remove this once
     # fixed upstream.
-    if build_frontend_env["CIBW_BUILD_FRONTEND"] == "build" and utils.platform == "windows":
+    if build_frontend_env["CIBW_BUILD_FRONTEND"] == "build" and utils.get_platform() == "windows":
         build_frontend_env["CIBW_SKIP"] = "gp*"
 
     # build the wheels
@@ -50,7 +50,7 @@ def test_pep518(tmp_path, build_frontend_env):
     # from a virtualenv seeded executable. See
     # https://github.com/oracle/graalpython/issues/491 and remove this once
     # fixed upstream.
-    if build_frontend_env["CIBW_BUILD_FRONTEND"] == "build" and utils.platform == "windows":
+    if build_frontend_env["CIBW_BUILD_FRONTEND"] == "build" and utils.get_platform() == "windows":
         expected_wheels = [w for w in expected_wheels if "graalpy" not in w]
 
     assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/test_troubleshooting.py b/test/test_troubleshooting.py
index 7039c2687..1b8e8a37f 100644
--- a/test/test_troubleshooting.py
+++ b/test/test_troubleshooting.py
@@ -15,7 +15,7 @@ def test_failed_build_with_so_files(tmp_path, capfd, build_frontend_env, project
     if project_contains_so_files:
         project.files["libnothing.so"] = ""
 
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("this test is only relevant to the linux build")
 
     project_dir = tmp_path / "project"
@@ -36,7 +36,7 @@ def test_failed_build_with_so_files(tmp_path, capfd, build_frontend_env, project
 
 @pytest.mark.parametrize("project_contains_so_files", [False, True])
 def test_failed_repair_with_so_files(tmp_path, capfd, project_contains_so_files):
-    if utils.platform != "linux":
+    if utils.get_platform() != "linux":
         pytest.skip("this test is only relevant to the linux build")
 
     project = new_c_project()
diff --git a/test/test_wheel_tag.py b/test/test_wheel_tag.py
index 580481ff9..09c99ccae 100644
--- a/test/test_wheel_tag.py
+++ b/test/test_wheel_tag.py
@@ -6,7 +6,7 @@
 
 
 def test_wheel_tag_is_correct_when_using_macosx_deployment_target(tmp_path):
-    if utils.platform != "macos":
+    if utils.get_platform() != "macos":
         pytest.skip("This test is only relevant to MACOSX_DEPLOYMENT_TARGET")
 
     project_dir = tmp_path / "project"
diff --git a/test/test_windows.py b/test/test_windows.py
index 142fccf25..073669ba7 100644
--- a/test/test_windows.py
+++ b/test/test_windows.py
@@ -39,7 +39,7 @@ def skip_if_no_msvc(arm64: bool = False) -> None:
 
 @pytest.mark.parametrize("use_pyproject_toml", [True, False])
 def test_wheel_tag_is_correct_when_using_windows_cross_compile(tmp_path, use_pyproject_toml):
-    if utils.platform != "windows":
+    if utils.get_platform() != "windows":
         pytest.skip("This test is only relevant to Windows")
 
     skip_if_no_msvc(arm64=True)
diff --git a/test/utils.py b/test/utils.py
index 1ed93642c..dfc66aabd 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -34,18 +34,29 @@
     CIProvider.cirrus_ci: False,
 }.get(detect_ci_provider(), True)
 
-platform = os.environ.get("CIBW_PLATFORM", "")
-if platform:
-    pass
-elif sys.platform.startswith("linux"):
-    platform = "linux"
-elif sys.platform.startswith("darwin"):
-    platform = "macos"
-elif sys.platform.startswith(("win32", "cygwin")):
-    platform = "windows"
-else:
-    msg = f"Unsupported platform {sys.platform!r}"
-    raise Exception(msg)
+
+def get_platform() -> str:
+    """Return the current platform as determined by CIBW_PLATFORM or sys.platform."""
+    platform = os.environ.get("CIBW_PLATFORM", "")
+    if platform:
+        return platform
+    elif sys.platform.startswith("linux"):
+        return "linux"
+    elif sys.platform.startswith("darwin"):
+        return "macos"
+    elif sys.platform.startswith(("win32", "cygwin")):
+        return "windows"
+    else:
+        msg = f"Unsupported platform {sys.platform!r}"
+        raise Exception(msg)
+
+
+DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease cpython-experimental-riscv64"
+
+
+def get_enable_groups() -> frozenset[EnableGroup]:
+    value = os.environ.get("CIBW_ENABLE", DEFAULT_CIBW_ENABLE)
+    return EnableGroup.parse_option_value(value)
 
 
 def cibuildwheel_get_build_identifiers(
@@ -74,7 +85,7 @@ def cibuildwheel_get_build_identifiers(
 def _update_pip_cache_dir(env: dict[str, str]) -> None:
     # Fix for pip concurrency bug https://github.com/pypa/pip/issues/11340
     # See https://github.com/pypa/cibuildwheel/issues/1254 for discussion.
-    if platform == "linux":
+    if get_platform() == "linux":
         return
     if "PIP_CACHE_DIR" in env:
         return
@@ -168,12 +179,12 @@ def expected_wheels(
     """
     if machine_arch is None:
         machine_arch = pm.machine()
-        if platform == "linux":
+        if get_platform() == "linux":
             machine_arch = arch_name_for_linux(machine_arch)
 
     architectures = [machine_arch]
     if not single_arch:
-        if platform == "linux":
+        if get_platform() == "linux":
             if machine_arch == "x86_64":
                 architectures.append("i686")
             elif (
@@ -182,7 +193,7 @@ def expected_wheels(
                 and _AARCH64_CAN_RUN_ARMV7
             ):
                 architectures.append("armv7l")
-        elif platform == "windows" and machine_arch == "AMD64":
+        elif get_platform() == "windows" and machine_arch == "AMD64":
             architectures.append("x86")
 
     return [
@@ -216,6 +227,8 @@ def _expected_wheels(
     """
     Returns a list of expected wheels from a run of cibuildwheel.
     """
+    platform = get_platform()
+
     # per PEP 425 (https://www.python.org/dev/peps/pep-0425/), wheel files shall have name of the form
     # {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
     # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
@@ -246,6 +259,7 @@ def _expected_wheels(
             "cp313-cp313",
         ]
 
+        enable_groups = get_enable_groups()
         if EnableGroup.CPythonFreeThreading in enable_groups:
             python_abi_tags.append("cp313-cp313t")
 
@@ -378,12 +392,12 @@ def get_xcode_version() -> tuple[int, int]:
 
 
 def skip_if_pyodide(reason: str) -> Any:
-    return pytest.mark.skipif(platform == "pyodide", reason=reason)
+    return pytest.mark.skipif(get_platform() == "pyodide", reason=reason)
 
 
 def invoke_pytest() -> str:
     # see https://github.com/pyodide/pyodide/issues/4802
-    if platform == "pyodide" and sys.platform.startswith("darwin"):
+    if get_platform() == "pyodide" and sys.platform.startswith("darwin"):
         return "python -m pytest"
     return "pytest"
 

From 898f0a45c106f5342c70b1e2fc29afadd28a81d5 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 19 May 2025 03:42:23 +0100
Subject: [PATCH 069/193] fix: change to the iOS testing option semantics
 (#2363)

* Change to the iOS testing option semantics

Don't assume the presence of `python -m` in the test command. Less magic
and allows more option reuse between platforms.

* Update schema

* Add a test for this warning

* Don't try to execute a test-command when it doesn't look like a module

* Update docs/options.md

Co-authored-by: Malcolm Smith <smith@chaquo.com>

* Only allow invalid test command if the first part is 'pytest'

* Responses to code review from @freakboy3742

* Fixes post-merge

---------

Co-authored-by: Malcolm Smith <smith@chaquo.com>
---
 cibuildwheel/platforms/ios.py |  41 +++++++++-
 cibuildwheel/util/helpers.py  |  15 ++++
 docs/options.md               |  11 ++-
 test/test_ios.py              | 136 ++++++++++++++++++++++------------
 test/utils.py                 |  60 +++++++++++----
 5 files changed, 195 insertions(+), 68 deletions(-)

diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 3473ef740..9e06ecd07 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -32,7 +32,7 @@
     download,
     move_file,
 )
-from ..util.helpers import prepare_command
+from ..util.helpers import prepare_command, unwrap_preserving_paragraphs
 from ..util.packaging import (
     combine_constraints,
     find_compatible_wheel,
@@ -593,6 +593,43 @@ def build(options: Options, tmp_path: Path) -> None:
                     )
 
                     log.step("Running test suite...")
+
+                    test_command_parts = shlex.split(build_options.test_command)
+                    if test_command_parts[0:2] != ["python", "-m"]:
+                        first_part = test_command_parts[0]
+                        if first_part == "pytest":
+                            # pytest works exactly the same as a module, so we
+                            # can just run it as a module.
+                            log.warning(
+                                unwrap_preserving_paragraphs(f"""
+                                    iOS tests configured with a test command which doesn't start
+                                    with 'python -m'. iOS tests must execute python modules - other
+                                    entrypoints are not supported.
+
+                                    cibuildwheel will try to execute it as if it started with
+                                    'python -m'. If this works, all you need to do is add that to
+                                    your test command.
+
+                                    Test command: {build_options.test_command!r}
+                                """)
+                            )
+                        else:
+                            msg = unwrap_preserving_paragraphs(
+                                f"""
+                                    iOS tests configured with a test command which doesn't start
+                                    with 'python -m'. iOS tests must execute python modules - other
+                                    entrypoints are not supported.
+
+                                    Test command: {build_options.test_command!r}
+                                """
+                            )
+                            raise errors.FatalError(msg)
+                    else:
+                        # the testbed run command actually doesn't want the
+                        # python -m prefix - it's implicit, so we remove it
+                        # here.
+                        test_command_parts = test_command_parts[2:]
+
                     try:
                         call(
                             "python",
@@ -600,7 +637,7 @@ def build(options: Options, tmp_path: Path) -> None:
                             "run",
                             *(["--verbose"] if build_options.build_verbosity > 0 else []),
                             "--",
-                            *(shlex.split(build_options.test_command)),
+                            *test_command_parts,
                             env=build_env,
                         )
                         failed = False
diff --git a/cibuildwheel/util/helpers.py b/cibuildwheel/util/helpers.py
index dbef452c0..363fd3345 100644
--- a/cibuildwheel/util/helpers.py
+++ b/cibuildwheel/util/helpers.py
@@ -76,6 +76,21 @@ def unwrap(text: str) -> str:
     return re.sub(r"\s+", " ", text)
 
 
+def unwrap_preserving_paragraphs(text: str) -> str:
+    """
+    Unwraps multi-line text to a single line, but preserves paragraphs
+    """
+    # remove initial line indent
+    text = textwrap.dedent(text)
+    # remove leading/trailing whitespace
+    text = text.strip()
+
+    paragraphs = text.split("\n\n")
+    # remove consecutive whitespace
+    paragraphs = [re.sub(r"\s+", " ", paragraph) for paragraph in paragraphs]
+    return "\n\n".join(paragraphs)
+
+
 def parse_key_value_string(
     key_value_string: str,
     positional_arg_names: Sequence[str] | None = None,
diff --git a/docs/options.md b/docs/options.md
index 2b8113970..d24db227b 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1253,7 +1253,7 @@ run your test suite.
 
 On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
 
-On iOS, the value of the `CIBW_TEST_COMMAND` setting is interpreted as the arguments to pass to `python -m` - that is, a Python module name, followed by arguments that will be assigned to `sys.argv`. Shell commands cannot be used.
+On iOS, the value of the `CIBW_TEST_COMMAND` setting must follow the format `python -m MODULE [ARGS...]` - where MODULE is a Python module name, followed by arguments that will be assigned to `sys.argv`. Other commands cannot be used.
 
 Platform-specific environment variables are also available:<br/>
 `CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE`
@@ -1273,6 +1273,10 @@ Platform-specific environment variables are also available:<br/>
     CIBW_TEST_COMMAND: >
       pytest ./tests &&
       python ./test.py
+
+    # run tests on ios
+    CIBW_TEST_SOURCES_IOS: tests
+    CIBW_TEST_COMMAND_IOS: python -m pytest ./tests
     ```
 
 !!! tab examples "pyproject.toml"
@@ -1290,6 +1294,11 @@ Platform-specific environment variables are also available:<br/>
       "pytest ./tests",
       "python ./test.py",
     ]
+
+    # run tests on ios
+    [tool.cibuildwheel.ios]
+    test-sources = ["tests"]
+    test-command = "python -m pytest ./tests"
     ```
 
     In configuration files, you can use an array, and the items will be joined with `&&`.
diff --git a/test/test_ios.py b/test/test_ios.py
index 7c5080da5..39d54ae91 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -4,6 +4,7 @@
 import platform
 import shutil
 import subprocess
+import textwrap
 
 import pytest
 
@@ -73,28 +74,17 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
             "CIBW_BUILD": "cp313-*",
             "CIBW_XBUILD_TOOLS": "does-exist",
             "CIBW_TEST_SOURCES": "tests",
-            "CIBW_TEST_COMMAND": "unittest discover tests test_platform.py",
+            "CIBW_TEST_COMMAND": "python -m unittest discover tests test_platform.py",
             "CIBW_BUILD_VERBOSITY": "1",
             **build_config,
         },
     )
 
     # The expected wheels were produced.
-    ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_")
-    platform_machine = platform.machine()
-
-    if platform_machine == "x86_64":
-        expected_wheels = {
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl",
-        }
-
-    elif platform_machine == "arm64":
-        expected_wheels = {
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl",
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl",
-        }
-
-    assert set(actual_wheels) == expected_wheels
+    expected_wheels = utils.expected_wheels(
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
+    )
+    assert set(actual_wheels) == set(expected_wheels)
 
     # The user was notified that the cross-build tool was found.
     captured = capfd.readouterr()
@@ -119,7 +109,7 @@ def test_no_test_sources(tmp_path, capfd):
             add_env={
                 "CIBW_PLATFORM": "ios",
                 "CIBW_BUILD": "cp313-*",
-                "CIBW_TEST_COMMAND": "tests",
+                "CIBW_TEST_COMMAND": "python -m tests",
             },
         )
 
@@ -146,7 +136,7 @@ def test_missing_xbuild_tool(tmp_path, capfd):
             add_env={
                 "CIBW_PLATFORM": "ios",
                 "CIBW_BUILD": "cp313-*",
-                "CIBW_TEST_COMMAND": "tests",
+                "CIBW_TEST_COMMAND": "python -m tests",
                 "CIBW_XBUILD_TOOLS": "does-not-exist",
             },
         )
@@ -180,21 +170,13 @@ def test_no_xbuild_tool_definition(tmp_path, capfd):
     )
 
     # The expected wheels were produced.
-    ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_")
-    platform_machine = platform.machine()
-
-    if platform_machine == "x86_64":
-        expected_wheels = {
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl",
-        }
-
-    elif platform_machine == "arm64":
-        expected_wheels = {
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl",
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl",
-        }
-
-    assert set(actual_wheels) == expected_wheels
+    expected_wheels = utils.expected_wheels(
+        "spam",
+        "0.1.0",
+        platform="ios",
+        python_abi_tags=["cp313-cp313"],
+    )
+    assert set(actual_wheels) == set(expected_wheels)
 
     # The user was notified that there was no cross-build tool definition.
     captured = capfd.readouterr()
@@ -225,23 +207,79 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd):
         },
     )
 
-    # The expected wheels were produced.
-    ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_")
-    platform_machine = platform.machine()
-
-    if platform_machine == "x86_64":
-        expected_wheels = {
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl",
-        }
-
-    elif platform_machine == "arm64":
-        expected_wheels = {
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl",
-            f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl",
-        }
-
-    assert set(actual_wheels) == expected_wheels
+    expected_wheels = utils.expected_wheels(
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
+    )
+    assert set(actual_wheels) == set(expected_wheels)
 
     # The warnings about cross-build notifications were silenced.
     captured = capfd.readouterr()
     assert "Your project configuration does not define any cross-build tools." not in captured.err
+
+
+@pytest.mark.serial
+def test_ios_test_command_without_python_dash_m(tmp_path, capfd):
+    """pytest should be able to run without python -m, but it should warn."""
+    if utils.get_platform() != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+
+    project = test_projects.new_c_project()
+    project.files["tests/__init__.py"] = ""
+    project.files["tests/test_spam.py"] = textwrap.dedent("""
+        import spam
+        def test_spam():
+            assert spam.filter("spam") == 0
+            assert spam.filter("ham") != 0
+    """)
+    project.generate(project_dir)
+
+    actual_wheels = utils.cibuildwheel_run(
+        project_dir,
+        add_env={
+            "CIBW_PLATFORM": "ios",
+            "CIBW_BUILD": "cp313-*",
+            "CIBW_TEST_COMMAND": "pytest ./tests",
+            "CIBW_TEST_SOURCES": "tests",
+            "CIBW_TEST_REQUIRES": "pytest",
+            "CIBW_XBUILD_TOOLS": "",
+        },
+    )
+
+    expected_wheels = utils.expected_wheels(
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
+    )
+    assert set(actual_wheels) == set(expected_wheels)
+
+    out, err = capfd.readouterr()
+
+    assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err
+
+
+def test_ios_test_command_invalid(tmp_path, capfd):
+    """Test command should raise an error if it's clearly invalid."""
+    if utils.get_platform() != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+    basic_project = test_projects.new_c_project()
+    basic_project.files["./my_test_script.sh"] = "echo hello"
+    basic_project.generate(project_dir)
+
+    with pytest.raises(subprocess.CalledProcessError):
+        utils.cibuildwheel_run(
+            project_dir,
+            add_env={
+                "CIBW_PLATFORM": "ios",
+                "CIBW_TEST_COMMAND": "./my_test_script.sh",
+                "CIBW_TEST_SOURCES": "./my_test_script.sh",
+                "CIBW_XBUILD_TOOLS": "",
+            },
+        )
+    out, err = capfd.readouterr()
+    assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err
diff --git a/test/utils.py b/test/utils.py
index dfc66aabd..028e88863 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -167,8 +167,10 @@ def expected_wheels(
     package_version: str,
     manylinux_versions: list[str] | None = None,
     musllinux_versions: list[str] | None = None,
-    macosx_deployment_target: str = "10.9",
+    macosx_deployment_target: str | None = None,
+    iphoneos_deployment_target: str | None = None,
     machine_arch: str | None = None,
+    platform: str | None = None,
     python_abi_tags: list[str] | None = None,
     include_universal2: bool = False,
     single_python: bool = False,
@@ -177,14 +179,22 @@ def expected_wheels(
     """
     Returns the expected wheels from a run of cibuildwheel.
     """
+    platform = platform or get_platform()
+
     if machine_arch is None:
         machine_arch = pm.machine()
-        if get_platform() == "linux":
+        if platform == "linux":
             machine_arch = arch_name_for_linux(machine_arch)
 
+    if macosx_deployment_target is None:
+        macosx_deployment_target = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "10.9")
+
+    if iphoneos_deployment_target is None:
+        iphoneos_deployment_target = os.environ.get("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
+
     architectures = [machine_arch]
     if not single_arch:
-        if get_platform() == "linux":
+        if platform == "linux":
             if machine_arch == "x86_64":
                 architectures.append("i686")
             elif (
@@ -193,22 +203,24 @@ def expected_wheels(
                 and _AARCH64_CAN_RUN_ARMV7
             ):
                 architectures.append("armv7l")
-        elif get_platform() == "windows" and machine_arch == "AMD64":
+        elif platform == "windows" and machine_arch == "AMD64":
             architectures.append("x86")
 
     return [
         wheel
         for architecture in architectures
         for wheel in _expected_wheels(
-            package_name,
-            package_version,
-            architecture,
-            manylinux_versions,
-            musllinux_versions,
-            macosx_deployment_target,
-            python_abi_tags,
-            include_universal2,
-            single_python,
+            package_name=package_name,
+            package_version=package_version,
+            machine_arch=architecture,
+            manylinux_versions=manylinux_versions,
+            musllinux_versions=musllinux_versions,
+            macosx_deployment_target=macosx_deployment_target,
+            iphoneos_deployment_target=iphoneos_deployment_target,
+            platform=platform,
+            python_abi_tags=python_abi_tags,
+            include_universal2=include_universal2,
+            single_python=single_python,
         )
     ]
 
@@ -220,6 +232,8 @@ def _expected_wheels(
     manylinux_versions: list[str] | None,
     musllinux_versions: list[str] | None,
     macosx_deployment_target: str,
+    iphoneos_deployment_target: str,
+    platform: str,
     python_abi_tags: list[str] | None,
     include_universal2: bool,
     single_python: bool,
@@ -227,8 +241,6 @@ def _expected_wheels(
     """
     Returns a list of expected wheels from a run of cibuildwheel.
     """
-    platform = get_platform()
-
     # per PEP 425 (https://www.python.org/dev/peps/pep-0425/), wheel files shall have name of the form
     # {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
     # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
@@ -249,7 +261,9 @@ def _expected_wheels(
 
     if platform == "pyodide" and python_abi_tags is None:
         python_abi_tags = ["cp312-cp312"]
-    if python_abi_tags is None:
+    elif platform == "ios" and python_abi_tags is None:
+        python_abi_tags = ["cp313-cp313"]
+    elif python_abi_tags is None:
         python_abi_tags = [
             "cp38-cp38",
             "cp39-cp39",
@@ -353,6 +367,20 @@ def _expected_wheels(
             if include_universal2:
                 platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2")
 
+        elif platform == "ios":
+            if machine_arch == "x86_64":
+                platform_tags = [
+                    f"ios_{iphoneos_deployment_target.replace('.', '_')}_x86_64_iphonesimulator"
+                ]
+            elif machine_arch == "arm64":
+                platform_tags = [
+                    f"ios_{iphoneos_deployment_target.replace('.', '_')}_arm64_iphoneos",
+                    f"ios_{iphoneos_deployment_target.replace('.', '_')}_arm64_iphonesimulator",
+                ]
+            else:
+                msg = f"Unsupported architecture {machine_arch!r} for iOS"
+                raise Exception(msg)
+
         elif platform == "pyodide":
             platform_tags = ["pyodide_2024_0_wasm32"]
 

From 1fe8e55c67c890e048366956a3e8b570e4d42e89 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 19 May 2025 08:52:26 +0100
Subject: [PATCH 070/193] [Bot] Update dependencies (#2400)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |  2 +-
 cibuildwheel/resources/nodejs.toml            |  4 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 docs/working-examples.md                      | 24 ++++-----
 4 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 5e1c9ca5c..702ea12d2 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -89,7 +89,7 @@ shellingham==1.5.4
     # via typer
 sniffio==1.3.1
     # via anyio
-typer==0.15.3
+typer==0.15.4
     # via
     #   auditwheel-emscripten
     #   pyodide-build
diff --git a/cibuildwheel/resources/nodejs.toml b/cibuildwheel/resources/nodejs.toml
index 9674bc39f..fc3fd2168 100644
--- a/cibuildwheel/resources/nodejs.toml
+++ b/cibuildwheel/resources/nodejs.toml
@@ -1,3 +1,3 @@
 url = "https://nodejs.org/dist/"
-v22 = "v22.15.0"
-v20 = "v20.19.1"
+v22 = "v22.15.1"
+v20 = "v20.19.2"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 1ff2bef8d..20956fba4 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.10-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.10-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.10-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.16-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.16-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.16-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.16-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.10-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.16-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.16-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.10-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.10-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.10-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.16-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.16-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.16-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.16-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.10-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.10-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.10-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.16-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.16-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.16-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.16-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.10-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.10-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.10-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.16-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.16-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.16-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.16-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.10-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.10-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.16-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.16-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.16-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.16-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.10-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.10-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.10-2
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.16-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.16-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.16-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.10-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.10-2
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.16-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.16-1
 
 [riscv64]
 
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 825b516e9..facc03ba0 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -52,8 +52,8 @@ title: Working examples
 | [PyYAML][]                        | ![github icon][] | ![apple icon][] | Canonical source repository for PyYAML |
 | [pikepdf][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python library for reading and writing PDF, powered by QPDF |
 | [numexpr][]                       | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast numerical array expression evaluator for Python, NumPy, Pandas, PyTables and more |
-| [Wrapt][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python module for decorators, wrappers and monkey patching. |
 | [h5py][]                          | ![azurepipelines icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | HDF5 for Python -- The h5py package is a Pythonic interface to the HDF5 binary data format. |
+| [Wrapt][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python module for decorators, wrappers and monkey patching. |
 | [envd][]                          | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A machine learning development environment build tool |
 | [Psycopg 3][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A modern implementation of a PostgreSQL adapter for Python |
 | [OpenColorIO][]                   | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. |
@@ -67,8 +67,8 @@ title: Working examples
 | [DeepForest][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | An Efficient, Scalable and Optimized Python Framework for Deep Forest (2021.2.1) |
 | [AutoPy][]                        | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. |
 | [H3-py][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for H3, a hierarchical hexagonal geospatial indexing system |
-| [mosec][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust |
 | [time-machine][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Time mocking library using only the CPython C API. |
+| [mosec][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust |
 | [Picologging][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. |
 | [pybind11 cmake_example][]        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system |
 | [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
@@ -76,8 +76,8 @@ title: Working examples
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
-| [python-rapidjson][]              | ![travisci icon][] ![gitlab icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson |
 | [pybind11 python_example][]       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
+| [python-rapidjson][]              | ![travisci icon][] ![gitlab icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson |
 | [sourmash][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
 | [abess][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
 | [python-snappy][]                 | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library |
@@ -97,15 +97,15 @@ title: Working examples
 | [bx-python][]                     | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
 | [pybase64][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
 | [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
-| [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
-| [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
+| [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
 | [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
+| [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
 | [Arbor][]                         | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries.  |
 | [clang-format][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. |
 | [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
-| [etebase-py][]                    | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. |
 | [ninja][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
+| [etebase-py][]                    | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. |
 | [cf-units][]                      | ![github icon][] | ![apple icon][] ![linux icon][] | Units of measure as required by the Climate and Forecast (CF) Metadata Conventions |
 | [numpythia][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
 | [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
@@ -162,8 +162,8 @@ title: Working examples
 [PyYAML]: https://github.com/yaml/pyyaml
 [pikepdf]: https://github.com/pikepdf/pikepdf
 [numexpr]: https://github.com/pydata/numexpr
-[Wrapt]: https://github.com/GrahamDumpleton/wrapt
 [h5py]: https://github.com/h5py/h5py
+[Wrapt]: https://github.com/GrahamDumpleton/wrapt
 [envd]: https://github.com/tensorchord/envd
 [Psycopg 3]: https://github.com/psycopg/psycopg
 [OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO
@@ -177,8 +177,8 @@ title: Working examples
 [DeepForest]: https://github.com/LAMDA-NJU/Deep-Forest
 [AutoPy]: https://github.com/autopilot-rs/autopy
 [H3-py]: https://github.com/uber/h3-py
-[mosec]: https://github.com/mosecorg/mosec
 [time-machine]: https://github.com/adamchainz/time-machine
+[mosec]: https://github.com/mosecorg/mosec
 [Picologging]: https://github.com/microsoft/picologging
 [pybind11 cmake_example]: https://github.com/pybind/cmake_example
 [markupsafe]: https://github.com/pallets/markupsafe
@@ -186,8 +186,8 @@ title: Working examples
 [KDEpy]: https://github.com/tommyod/KDEpy
 [dd-trace-py]: https://github.com/DataDog/dd-trace-py
 [tgcalls]: https://github.com/MarshalX/tgcalls
-[python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson
 [pybind11 python_example]: https://github.com/pybind/python_example
+[python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson
 [sourmash]: https://github.com/sourmash-bio/sourmash
 [abess]: https://github.com/abess-team/abess
 [python-snappy]: https://github.com/intake/python-snappy
@@ -207,15 +207,15 @@ title: Working examples
 [bx-python]: https://github.com/bxlab/bx-python
 [pybase64]: https://github.com/mayeut/pybase64
 [boost-histogram]: https://github.com/scikit-hep/boost-histogram
-[Python-WebRTC]: https://github.com/MarshalX/python-webrtc
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
-[fathon]: https://github.com/stfbnc/fathon
+[Python-WebRTC]: https://github.com/MarshalX/python-webrtc
 [pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
+[fathon]: https://github.com/stfbnc/fathon
 [Arbor]: https://github.com/arbor-sim/arbor
 [clang-format]: https://github.com/ssciwr/clang-format-wheel
 [polaroid]: https://github.com/daggy1234/polaroid
-[etebase-py]: https://github.com/etesync/etebase-py
 [ninja]: https://github.com/scikit-build/ninja-python-distributions
+[etebase-py]: https://github.com/etesync/etebase-py
 [cf-units]: https://github.com/SciTools/cf-units
 [numpythia]: https://github.com/scikit-hep/numpythia
 [pyjet]: https://github.com/scikit-hep/pyjet

From e46e9516d96d9e4857c94eb5c7c0bea83b0d6c45 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 19 May 2025 09:04:40 +0100
Subject: [PATCH 071/193] Fix release script

---
 bin/bump_version.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/bin/bump_version.py b/bin/bump_version.py
index 9e1a3592d..452dfa066 100755
--- a/bin/bump_version.py
+++ b/bin/bump_version.py
@@ -22,8 +22,6 @@
     ("cibuildwheel/__init__.py", '__version__ = "{}"'),
     ("docs/faq.md", "cibuildwheel=={}"),
     ("docs/faq.md", "cibuildwheel@v{}"),
-    ("docs/setup.md", "cibuildwheel=={}"),
-    ("docs/ci-services.md", "cibuildwheel=={}"),
     ("examples/*", "cibuildwheel=={}"),
     ("examples/*", "cibuildwheel@v{}"),
 ]

From 90a0ddeff0f23eebc21630e65d66d0f4955e9b94 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 19 May 2025 09:29:14 +0100
Subject: [PATCH 072/193] Bump version: v3.0.0b1

---
 README.md                              | 51 ++++++++++++--------------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      | 22 +++++++++++
 docs/faq.md                            |  6 +--
 examples/azure-pipelines-minimal.yml   |  6 +--
 examples/circleci-minimal.yml          |  6 +--
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +--
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 +-
 pyproject.toml                         |  2 +-
 18 files changed, 70 insertions(+), 53 deletions(-)

diff --git a/README.md b/README.md
index bb046a070..e20e0a6fc 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==2.23.3
+        run: python -m pip install cibuildwheel==3.0.0b1
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -217,6 +217,27 @@ Changelog
 
 <!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
 
+### v3.0.0
+
+Not yet released, but available for testing
+
+#### v3.0.0b1
+
+_19 May 2025_
+
+- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/latest/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/latest/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out!
+- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable). (#1538)
+- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b1.
+
+    _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources). \[discussion about the test cwd change and how to use to come!\]
+- ✨ Added `dependency-versions` inline syntax (#2123)
+- 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
+- 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
+- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`.
+- ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
+- 📚 A reorganisation of the docs, and numerous updates (#2280)
+
 ### v2.23.3
 
 _26 April 2025_
@@ -229,6 +250,7 @@ _24 March 2025_
 
 - 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
 - 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
+-
 
 ### v2.23.1
 
@@ -237,33 +259,6 @@ _15 March 2025_
 - ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
 - 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
 
-### v2.23.0
-
-_1 March 2025_
-
-- ✨ Adds official support for the new GitHub Actions Arm runners. In fact these worked out-of-the-box, now we include them in our tests and example configs. (#2135 via #2281)
-- ✨ Adds support for building PyPy 3.11 wheels (#2268 via #2281)
-- 🛠 Adopts the beta pypa/manylinux image for armv7l builds (#2269 via #2281)
-- 🛠 Dependency updates, including Pyodide 0.27 (#2117 and #2281)
-
-### v2.22.0
-
-_23 November 2024_
-
-- 🌟 Added a new `CIBW_ENABLE`/`enable` feature that replaces `CIBW_FREETHREADED_SUPPORT`/`free-threaded-support` and `CIBW_PRERELEASE_PYTHONS` with a system that supports both. In cibuildwheel 3, this will also include a PyPy setting and the deprecated options will be removed. (#2048)
-- 🌟 [Dependency groups](https://peps.python.org/pep-0735/) are now supported for tests. Use `CIBW_TEST_GROUPS`/`test-groups` to specify groups in `[dependency-groups]` for testing. (#2063)
-- 🌟 Support for the experimental Ubuntu-based ARMv7l manylinux image (#2052)
-- ✨ Show a warning when cibuildwheel is run from Python 3.10 or older; cibuildwheel 3.0 will require Python 3.11 or newer as host (#2050)
-- 🐛 Fix issue with stderr interfering with checking the docker version (#2074)
-- 🛠 Python 3.9 is now used in `CIBW_BEFORE_ALL`/`before-all` on linux, replacing 3.8, which is now EoL (#2043)
-- 🛠 Error messages for producing a pure-Python wheel are slightly more informative (#2044)
-- 🛠 Better error when `uname -m` fails on ARM (#2049)
-- 🛠 Better error when repair fails and docs for abi3audit on Windows (#2058)
-- 🛠 Better error when `manylinux-interpreters ensure` fails (#2066)
-- 🛠 Update Pyodide to 0.26.4, and adapt to the unbundled pyodide-build (now 0.29) (#2090)
-- 🛠 Now cibuildwheel uses dependency-groups for development dependencies (#2064, #2085)
-- 📚 Docs updates and tidy ups (#2061, #2067, #2072)
-
 <!-- END bin/update_readme_changelog.py -->
 
 ---
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index c142a69b0..3996ce876 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "2.23.3"
+__version__ = "3.0.0b1"
diff --git a/docs/changelog.md b/docs/changelog.md
index a633ea807..38009b370 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,27 @@ title: Changelog
 
 # Changelog
 
+### v3.0.0
+
+Not yet released, but available for testing
+
+#### v3.0.0b1
+
+_19 May 2025_
+
+- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/latest/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/latest/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out!
+- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable). (#1538)
+- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b1.
+
+    _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources). \[discussion about the test cwd change and how to use to come!\]
+- ✨ Added `dependency-versions` inline syntax (#2123)
+- 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
+- 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
+- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`.
+- ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
+- 📚 A reorganisation of the docs, and numerous updates (#2280)
+
 ### v2.23.3
 
 _26 April 2025_
@@ -16,6 +37,7 @@ _24 March 2025_
 
 - 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
 - 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
+-
 
 ### v2.23.1
 
diff --git a/docs/faq.md b/docs/faq.md
index 1d968d657..44c914786 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v2.23.3
+uses: pypa/cibuildwheel@v3.0.0b1
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==2.23.3
+cibuildwheel==3.0.0b1
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==2.23.3 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0b1 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index db66bfddd..fa727b9a9 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==2.23.3
+        pip3 install cibuildwheel==3.0.0b1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==2.23.3
+        python3 -m pip install cibuildwheel==3.0.0b1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==2.23.3
+        pip install cibuildwheel==3.0.0b1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 426cd648b..21a8b5dfa 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.3
+            python3 -m pip install --user cibuildwheel==3.0.0b1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==2.23.3
+            python3 -m pip install --user cibuildwheel==3.0.0b1
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==2.23.3
+            pip3 install cibuildwheel==3.0.0b1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 324f075fb..dfc75146d 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.3
+    - python -m pip install cibuildwheel==3.0.0b1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 8f69603cf..f601f3f30 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==2.23.3
+    - python -m pip install cibuildwheel==3.0.0b1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index dee716845..4888f19e9 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.3
+        uses: pypa/cibuildwheel@v3.0.0b1
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index c4c154a0a..a0102bb0f 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.3
+        uses: pypa/cibuildwheel@v3.0.0b1
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index 0dc07457f..14775233a 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==2.23.3
+        run: pipx run cibuildwheel==3.0.0b1
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 91f3be9de..c0325f9b8 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.23.3
+        uses: pypa/cibuildwheel@v3.0.0b1
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 7441a0e63..40ef7e3d5 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==2.23.3
+    - python -m pip install cibuildwheel==3.0.0b1
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==2.23.3
+    - py -m pip install cibuildwheel==3.0.0b1
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==2.23.3
+    - python3 -m pip install cibuildwheel==3.0.0b1
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 2a6bc55ae..abb1b682d 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==2.23.3
+    - python -m pip install cibuildwheel==3.0.0b1
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index a7c24a318..7a307e706 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.3
+  - python3 -m pip install cibuildwheel==3.0.0b1
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index f4592899a..8b6077435 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==2.23.3
+  - python3 -m pip install cibuildwheel==3.0.0b1
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 0725a74fb..9f3176575 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==2.23.3 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==2.23.3 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 18585c549..cb5ae27a6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "2.23.3"
+version = "3.0.0b1"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From bd641d11deff8220530f9ceb03e7b9a7a8a660b3 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 19 May 2025 10:02:39 +0100
Subject: [PATCH 073/193] Add docs note

---
 README.md         | 4 +++-
 docs/changelog.md | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index e20e0a6fc..7f609295b 100644
--- a/README.md
+++ b/README.md
@@ -219,7 +219,9 @@ Changelog
 
 ### v3.0.0
 
-Not yet released, but available for testing
+Not yet released, but available for testing.
+
+Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
 #### v3.0.0b1
 
diff --git a/docs/changelog.md b/docs/changelog.md
index 38009b370..0ae1f7d1f 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -6,7 +6,9 @@ title: Changelog
 
 ### v3.0.0
 
-Not yet released, but available for testing
+Not yet released, but available for testing.
+
+Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
 #### v3.0.0b1
 

From 9dd7192d89877be92c53db5f1035cca2ae801241 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 19 May 2025 16:58:28 +0100
Subject: [PATCH 074/193] ci: fix failing tests due to rate-limiting (#2401)

raw.githubusercontent.com URLs are rate-limited now, change URLs
---
 test/test_ssl.py           | 4 ++--
 unit_test/download_test.py | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/test_ssl.py b/test/test_ssl.py
index 89ad4b80a..e4ff24e91 100644
--- a/test/test_ssl.py
+++ b/test/test_ssl.py
@@ -10,9 +10,9 @@
         from urllib.request import urlopen
 
         context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
+        # badssl.com is a HTTPS test server that can be used to test SSL connections
         data = urlopen("https://tls-v1-2.badssl.com", context=context)
-        data = urlopen("https://raw.githubusercontent.com/pypa/cibuildwheel/main/CI.md", context=context)
-        data = urlopen("https://raw.githubusercontent.com/pypa/cibuildwheel/main/CI.md")
+        data = urlopen("https://tls-v1-2.badssl.com")
         """
     )
 )
diff --git a/unit_test/download_test.py b/unit_test/download_test.py
index ca54e9dca..4c5f9edc8 100644
--- a/unit_test/download_test.py
+++ b/unit_test/download_test.py
@@ -5,7 +5,7 @@
 
 from cibuildwheel.util.file import download
 
-DOWNLOAD_URL = "https://raw.githubusercontent.com/pypa/cibuildwheel/v1.6.3/requirements-dev.txt"
+DOWNLOAD_URL = "https://cdn.jsdelivr.net/gh/pypa/cibuildwheel@v1.6.3/requirements-dev.txt"
 
 
 def test_download(monkeypatch, tmp_path):

From fcdbed860ec362051a8fbd7e411610c460f63309 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Tue, 20 May 2025 13:41:26 +0100
Subject: [PATCH 075/193] Add TEST_ENVIRONMENT option and set PYTHONSAFEPATH=1
 in the test env (#2388)

* Add TEST_ENVIRONMENT option and set PYTHONSAFEPATH=1 in the test env

Closes https://github.com/pypa/cibuildwheel/issues/2358

* Add toml docs examples

* Add TOML examples

* Use double-quotes for windows
---
 bin/generate_schema.py                        |  4 ++
 cibuildwheel/options.py                       | 11 ++++++
 cibuildwheel/platforms/ios.py                 | 14 ++++---
 cibuildwheel/platforms/linux.py               |  4 ++
 cibuildwheel/platforms/macos.py               |  7 ++++
 cibuildwheel/platforms/pyodide.py             |  6 +++
 cibuildwheel/platforms/windows.py             |  7 ++++
 .../resources/cibuildwheel.schema.json        | 39 +++++++++++++++++++
 cibuildwheel/resources/defaults.toml          |  1 +
 docs/options.md                               | 36 +++++++++++++++++
 test/test_testing.py                          | 22 +++++++++++
 11 files changed, 146 insertions(+), 5 deletions(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index ac97f8e9b..116a0aeb9 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -221,6 +221,9 @@
   test-skip:
     description: Skip running tests on some builds.
     type: string_array
+  test-environment:
+    description: Set environment variables for the test environment
+    type: string_table
 """
 
 schema = yaml.safe_load(starter)
@@ -300,6 +303,7 @@
         test-extras: {"$ref": "#/$defs/inherit"}
         test-sources: {"$ref": "#/$defs/inherit"}
         test-requires: {"$ref": "#/$defs/inherit"}
+        test-environment: {"$ref": "#/$defs/inherit"}
 """
 )
 
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index fa1d1c046..94ffcefcb 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -107,6 +107,7 @@ class BuildOptions:
     test_requires: list[str]
     test_extras: str
     test_groups: list[str]
+    test_environment: ParsedEnvironment
     build_verbosity: int
     build_frontend: BuildFrontendConfig | None
     config_settings: str
@@ -739,6 +740,15 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                     "test-sources", option_format=ListFormat(sep=" ", quote=shlex.quote)
                 )
             )
+            test_environment_config = self.reader.get(
+                "test-environment", option_format=EnvironmentFormat()
+            )
+            try:
+                test_environment = parse_environment(test_environment_config)
+            except (EnvironmentParseError, ValueError) as e:
+                msg = f"Malformed environment option {test_environment_config!r}"
+                raise errors.ConfigurationError(msg) from e
+
             test_requires = self.reader.get(
                 "test-requires", option_format=ListFormat(sep=" ")
             ).split()
@@ -844,6 +854,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                 globals=self.globals,
                 test_command=test_command,
                 test_sources=test_sources,
+                test_environment=test_environment,
                 test_requires=[*test_requires, *test_requirements_from_groups],
                 test_extras=test_extras,
                 test_groups=test_groups,
diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 9e06ecd07..dc4911f75 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -536,13 +536,17 @@ def build(options: Options, tmp_path: Path) -> None:
                 elif config.arch != os.uname().machine:
                     log.step("Skipping tests on non-native simulator architecture")
                 else:
+                    test_env = build_options.test_environment.as_dictionary(
+                        prev_environment=build_env
+                    )
+
                     if build_options.before_test:
                         before_test_prepared = prepare_command(
                             build_options.before_test,
                             project=".",
                             package=build_options.package_dir,
                         )
-                        shell(before_test_prepared, env=env)
+                        shell(before_test_prepared, env=test_env)
 
                     log.step("Setting up test harness...")
                     # Clone the testbed project into the build directory
@@ -552,7 +556,7 @@ def build(options: Options, tmp_path: Path) -> None:
                         target_install_path / "testbed",
                         "clone",
                         testbed_path,
-                        env=build_env,
+                        env=test_env,
                     )
 
                     if not build_options.test_sources:
@@ -574,7 +578,7 @@ def build(options: Options, tmp_path: Path) -> None:
                     # the test requirements. Use the --platform tag to force
                     # the installation of iOS wheels; this requires the use of
                     # --only-binary=:all:
-                    ios_version = build_env["IPHONEOS_DEPLOYMENT_TARGET"]
+                    ios_version = test_env["IPHONEOS_DEPLOYMENT_TARGET"]
                     platform_tag = f"ios_{ios_version.replace('.', '_')}_{config.arch}_{config.sdk}"
 
                     call(
@@ -589,7 +593,7 @@ def build(options: Options, tmp_path: Path) -> None:
                         testbed_path / "iOSTestbed" / "app_packages",
                         f"{test_wheel}{build_options.test_extras}",
                         *build_options.test_requires,
-                        env=build_env,
+                        env=test_env,
                     )
 
                     log.step("Running test suite...")
@@ -638,7 +642,7 @@ def build(options: Options, tmp_path: Path) -> None:
                             *(["--verbose"] if build_options.build_verbosity > 0 else []),
                             "--",
                             *test_command_parts,
-                            env=build_env,
+                            env=test_env,
                         )
                         failed = False
                     except subprocess.CalledProcessError:
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 28650525e..5ffd5a2ef 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -361,6 +361,10 @@ def build_in_container(
             virtualenv_env = env.copy()
             virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
             virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
+            virtualenv_env["PYTHONSAFEPATH"] = "1"
+            virtualenv_env = build_options.test_environment.as_dictionary(
+                prev_environment=virtualenv_env
+            )
 
             if build_options.before_test:
                 before_test_prepared = prepare_command(
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index e2f5b9115..d64917368 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -648,6 +648,13 @@ def build(options: Options, tmp_path: Path) -> None:
 
                     virtualenv_env["MACOSX_DEPLOYMENT_TARGET"] = get_test_macosx_deployment_target()
 
+                    # see https://github.com/pypa/cibuildwheel/issues/2358 for discussion
+                    virtualenv_env["PYTHONSAFEPATH"] = "1"
+
+                    virtualenv_env = build_options.test_environment.as_dictionary(
+                        prev_environment=virtualenv_env
+                    )
+
                     # check that we are using the Python from the virtual environment
                     call_with_arch("which", "python", env=virtualenv_env)
 
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index 1c05dd0cd..77e3b9a81 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -380,6 +380,12 @@ def build(options: Options, tmp_path: Path) -> None:
                 )
                 virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
 
+                virtualenv_env["PYTHONSAFEPATH"] = "1"
+
+                virtualenv_env = build_options.test_environment.as_dictionary(
+                    prev_environment=virtualenv_env
+                )
+
                 # check that we are using the Python from the virtual environment
                 call("which", "python", env=virtualenv_env)
 
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 91e72404b..4e59275bf 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -558,6 +558,13 @@ def build(options: Options, tmp_path: Path) -> None:
                     pip_version=pip_version,
                 )
 
+                # see https://github.com/pypa/cibuildwheel/issues/2358 for discussion
+                virtualenv_env["PYTHONSAFEPATH"] = "1"
+
+                virtualenv_env = build_options.test_environment.as_dictionary(
+                    prev_environment=virtualenv_env
+                )
+
                 # check that we are using the Python from the virtual environment
                 call("where", "python", env=virtualenv_env)
 
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 8ffa0b75e..6a71543bf 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -544,6 +544,24 @@
       ],
       "title": "CIBW_TEST_SKIP"
     },
+    "test-environment": {
+      "description": "Set environment variables for the test environment",
+      "oneOf": [
+        {
+          "type": "string"
+        },
+        {
+          "type": "object",
+          "additionalProperties": false,
+          "patternProperties": {
+            ".+": {
+              "type": "string"
+            }
+          }
+        }
+      ],
+      "title": "CIBW_TEST_ENVIRONMENT"
+    },
     "overrides": {
       "type": "array",
       "description": "An overrides array",
@@ -610,6 +628,9 @@
               },
               "test-requires": {
                 "$ref": "#/$defs/inherit"
+              },
+              "test-environment": {
+                "$ref": "#/$defs/inherit"
               }
             }
           },
@@ -714,6 +735,9 @@
           },
           "test-requires": {
             "$ref": "#/properties/test-requires"
+          },
+          "test-environment": {
+            "$ref": "#/properties/test-environment"
           }
         }
       }
@@ -836,6 +860,9 @@
         },
         "test-requires": {
           "$ref": "#/properties/test-requires"
+        },
+        "test-environment": {
+          "$ref": "#/properties/test-environment"
         }
       }
     },
@@ -890,6 +917,9 @@
         },
         "test-requires": {
           "$ref": "#/properties/test-requires"
+        },
+        "test-environment": {
+          "$ref": "#/properties/test-environment"
         }
       }
     },
@@ -957,6 +987,9 @@
         },
         "test-requires": {
           "$ref": "#/properties/test-requires"
+        },
+        "test-environment": {
+          "$ref": "#/properties/test-environment"
         }
       }
     },
@@ -1011,6 +1044,9 @@
         },
         "test-requires": {
           "$ref": "#/properties/test-requires"
+        },
+        "test-environment": {
+          "$ref": "#/properties/test-environment"
         }
       }
     },
@@ -1065,6 +1101,9 @@
         },
         "test-requires": {
           "$ref": "#/properties/test-requires"
+        },
+        "test-environment": {
+          "$ref": "#/properties/test-environment"
         }
       }
     }
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 4d03394fd..f0010806e 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -24,6 +24,7 @@ test-sources = []
 test-requires = []
 test-extras = []
 test-groups = []
+test-environment = {}
 
 container-engine = "docker"
 
diff --git a/docs/options.md b/docs/options.md
index d24db227b..1f4d4d120 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1539,6 +1539,42 @@ This option is not supported in the overrides section in `pyproject.toml`.
     test-skip = "*-macosx_arm64 *-macosx_universal2:arm64"
     ```
 
+### `CIBW_TEST_ENVIRONMENT` {: #test-environment}
+
+> Set environment variables for the test environment
+
+A space-separated list of environment variables to set in the test environment.
+
+The syntax is the same as for [`CIBW_ENVIRONMENT`](#environment).
+
+cibuildwheel sets the variable [`PYTHONSAFEPATH`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSAFEPATH) to avoid picking up in-tree dependencies when running the tests - we want to test the installed wheel, not the in-tree version. However, this can be overridden by setting `PYTHONSAFEPATH` to an empty string.
+
+Platform-specific environment variables are also available:<br/>
+`CIBW_TEST_ENVIRONMENT_MACOS` | `CIBW_TEST_ENVIRONMENT_WINDOWS` | `CIBW_TEST_ENVIRONMENT_LINUX` | `CIBW_TEST_ENVIRONMENT_IOS` | `CIBW_TEST_ENVIRONMENT_PYODIDE`
+
+#### Examples
+
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Set the environment variable MY_ENV_VAR to "my_value" in the test environment
+    CIBW_TEST_ENVIRONMENT: MY_ENV_VAR=my_value
+
+    # Unset PYTHONSAFEPATH in the test environment
+    CIBW_TEST_ENVIRONMENT: PYTHONSAFEPATH=
+    ```
+
+!!! tab examples "pyproject.toml"
+
+    ```toml
+    [tool.cibuildwheel]
+    # Set the environment variable MY_ENV_VAR to "my_value" in the test environment
+    test-environment = { MY_ENV_VAR="my_value" }
+
+    # Unset PYTHONSAFEPATH in the test environment
+    test-environment = { PYTHONSAFEPATH="" }
+    ```
+
 ## Debugging
 
 ### `CIBW_DEBUG_KEEP_CONTAINER`
diff --git a/test/test_testing.py b/test/test_testing.py
index 9b7364f99..5caa35e42 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -66,6 +66,12 @@ def test_uname(self):
         bits = struct.calcsize("P") * 8
         if bits == 32:
             self.assertIn(platform.machine(), ["i686", "armv7l","armv8l", "wasm32"])
+
+    def test_pythonsafepath(self):
+        # check that the PYTHONSAFEPATH environment variable is set
+        value = os.environ.get("PYTHONSAFEPATH")
+        self.assertIsNotNone(value)
+        self.assertNotEqual(value, "")
 '''
 
 
@@ -229,3 +235,19 @@ def test_test_sources(tmp_path):
     # also check that we got the right wheels
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
     assert set(actual_wheels) == set(expected_wheels)
+
+
+def test_test_environment(tmp_path):
+    project_dir = tmp_path / "project"
+    test_projects.new_c_project().generate(project_dir)
+
+    actual_wheels = utils.cibuildwheel_run(
+        project_dir,
+        add_env={
+            "CIBW_TEST_ENVIRONMENT": "MYVAR=somevalue PYTHONSAFEPATH=",
+            "CIBW_TEST_COMMAND": "python -c \"import os; assert os.environ.get('MYVAR') == 'somevalue'; assert os.environ.get('PYTHONSAFEPATH') == ''\"",
+        },
+    )
+    # also check that we got the right wheels
+    expected_wheels = utils.expected_wheels("spam", "0.1.0")
+    assert set(actual_wheels) == set(expected_wheels)

From c3c260dff7f0543dd9ba6666cf85264b999bc017 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 20 May 2025 13:43:19 +0100
Subject: [PATCH 076/193] [pre-commit.ci] pre-commit autoupdate (#2403)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.9 → v0.11.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.9...v0.11.10)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7d2c65182..2705e71a6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.9
+  rev: v0.11.10
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 163864158d0ee5319d281a1ddfafd43a13e987cd Mon Sep 17 00:00:00 2001
From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com>
Date: Wed, 21 May 2025 02:29:58 +0530
Subject: [PATCH 077/193] feat: Pyodide improvements: version setting,
 standalone environments (#2002)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fix a typo: pyoodide ➡️ pyodide

* Add `pyodide_build_version` attribute

* Add version to xbuildenv log step

* Add version to Emscripten log step

* Use `pyodide-build`'s version for updating constraints

* Bump Pyodide constraints by updating `pyodide-build`

* Add a schema for `pyodide-version`

* Update Pyodide constraints

* Bump `pyodide-build` to new 0.29.0

* Test out another Pyodide identifier

* Update outdated Pyodide constraints

* Add Pyodide version to temp directory name

* Remove Pyodide 0.26.1 from build configurations

* Retrieve + validate + install specific xbuildenvs

* Test wheel builds with Pyodide 0.26.2

* Add correct Pyodide version to identifier temp dir

* Don't pre-call Pyodide xbuildenv search

* Fetch just the stable Pyodide versions

* Refactor search + validation + install into one step

* Move all of it under a lock

* Reorder xbuildenv installation

* Add env and cwd to xbuildenv search call

* Temporarily lower to 0.26.2 target

* Separate out search, validate, install; again

* Run xbuildenv search in `CIBW_CACHE_PATH`

* Remove prior `PYODIDE_ROOT` env vars, copy envs

* Validate doesn't need to depend on searching

* Add file lock when searching xbuildenvs

* Test the original version: 0.26.1

* Update Pyodide constraints

* Update constraints for `pyodide-build` 0.29.0 again

* Bump Pyodide from version 0.26.1 ➡️ version 0.26.4

* Add note on compatibility for macOS + other archs

* Note Pyodide version for Pyodide identifier

* Docs about `CIBW_PYODIDE_VERSION`

* Don't fetch just the stable versions

* Discard a variable that's not used later

* Rename `search_xbuildenv` ➡️ `get_xbuildenv_versions`

* `validate_xbuildenv` ➡️ `validate_xbuildenv_version`

* Replace ordered comment, add newline

* Replace sentence on macOS support

Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com>

* Capitalise: "pyodide" ➡️ "Pyodide"

Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com>

* "work" ➡️ "may succeed"

Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com>

* Add another job to test a custom Pyodide version

* Handle "v"-prefixed + non-prefixed versions

* Convert to a proper toml-able option, and remove some hardcoded versions

This removes the enscripten and pyodide-build version specs from
pyproject.toml - pyodide-build is spec'd in the constraints file, and
the emscripten version can be read from the pyodide-build output.

* Add a schema entry

* Add docs for CIBW_PYODIDE_VERSION

* Rephrase

* Add tests for pyodide-version

* Apply suggestions from code review

* Add python_build_standalone util

* Hook up to python-build-standalone, removing dependency on host python

* Remove python hard-code in action.yml

* Add log step

* Add workaround for https://github.com/pyodide/pyodide-build/issues/143

* Add emscripten pytest test

* Remove unneeded checks

* Fix a pytest invoke for emscripten

* Generate pyodide-build constraints from the pinned pyodide version

* Remove pyodide python-build-standalone workaround

* Fixup paths from newer version of pyodide-build

* Fix/skip some failing tests

* Use `python -m pytest` on pyodide, even on Linux

* Docs fixes

* Don't call the github API at runtime, cache the release assets instead

* Ignore pylint false positive

* Add version auto-updating for pyodide

* Add support for pyodide 3.13.

* Fix tests for multiple pyodide wheels

* Remove workaround for unreleased pyodide-build

* Rename to "test_pyodide"

* Fix pathname confusion

* Remove extra github actions job

* Fix expectation for test_abi_none

* Fix the custom_repair_wheel test to actually have clashing names

* Fix pinned version test

* Document test-command limitation

* Remove pyodide 0.28.0a1 for now

* Update constraints files

* Docs/test fixes post removing pyodide cp313

* Fix ABI test expectation

* Docs improvements

* Improve some comments

* Remove logic duplication

* remove pyodide special casing

* Refactor constraints code to use a utility script, circumventing import issues

* chore: nicer nox env

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: typo in variable name found by copilot

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

* Some more Pyodide version updates in the docs section

* We haven't released Pyodide v0.27.6 yet

* Back to the Github URL for cross-build-environments

* `pyodide-build`, not `emsdk` for Windows skips

Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com>

* Move to a separate `_json_request` function

Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com>

* Rename "retries" ➡️ "retry_count"

* Add some type hints

* Copy env vars before `UV_CUSTOM_COMPILE_COMMAND`

* Use `HTTPError.headers.get` instead

* Remove extra end quote

* Change download tests URL to `https://badssl.com/`

Co-Authored-By: Joe Rickerby <1244307+joerick@users.noreply.github.com>

* Download size changes, too

* Use jsdelivr for Github asset mirroring

* Bump to Pyodide v0.27.6

* Fix unit tests

* Move to pyodide v0.27.6 again

* Bump to pyodide-build 0.30.4

* Use new URL for cross-build environments metadata

Co-authored-by: Joe Rickerby <joerick@mac.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <1244307+joerick@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .github/workflows/test.yml                    |  15 +-
 action.yml                                    |   3 +-
 bin/generate_pyodide_constraints.py           |  69 +++
 bin/generate_schema.py                        |   3 +
 bin/update_python_build_standalone.py         |  59 +++
 bin/update_pythons.py                         |  53 ++-
 cibuildwheel/extra.py                         |  71 ++-
 cibuildwheel/options.py                       |   4 +
 cibuildwheel/platforms/pyodide.py             | 184 ++++++--
 cibuildwheel/resources/build-platforms.toml   |   2 +-
 .../resources/cibuildwheel.schema.json        |  23 +
 .../resources/constraints-pyodide312.txt      |  12 +-
 cibuildwheel/resources/defaults.toml          |   2 +
 .../python-build-standalone-releases.json     | 425 ++++++++++++++++++
 cibuildwheel/util/packaging.py                |   3 +
 cibuildwheel/util/python_build_standalone.py  | 181 ++++++++
 cibuildwheel/util/resources.py                |   1 +
 docs/options.md                               |  73 ++-
 docs/platforms.md                             |  20 +-
 noxfile.py                                    |  30 +-
 test/test_abi_variants.py                     |  19 +-
 test/test_custom_repair_wheel.py              |  12 +-
 test/test_dependency_versions.py              |   5 +-
 test/test_environment.py                      |   7 +-
 test/test_pep518.py                           |  13 +-
 test/{test_emscripten.py => test_pyodide.py}  |  73 ++-
 test/test_testing.py                          |   2 +-
 test/utils.py                                 |  12 +-
 unit_test/options_test.py                     |   5 +
 unit_test/utils_test.py                       |  32 ++
 30 files changed, 1298 insertions(+), 115 deletions(-)
 create mode 100755 bin/generate_pyodide_constraints.py
 create mode 100755 bin/update_python_build_standalone.py
 create mode 100644 cibuildwheel/resources/python-build-standalone-releases.json
 create mode 100644 cibuildwheel/util/python_build_standalone.py
 rename test/{test_emscripten.py => test_pyodide.py} (52%)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 92d2d659b..684c76cf4 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -195,7 +195,7 @@ jobs:
       run: uv run --no-sync pytest --run-emulation ${{ matrix.arch }} test/test_emulation.py
 
   test-pyodide:
-    name: Test cibuildwheel building Pyodide wheels
+    name: Test pyodide
     needs: lint
     runs-on: ubuntu-24.04
     timeout-minutes: 180
@@ -222,6 +222,19 @@ jobs:
       env:
         CIBW_PLATFORM: pyodide
 
+    - name: Run a sample build (GitHub Action) for an overridden Pyodide version
+      uses: ./
+      with:
+        package-dir: sample_proj
+        output-dir: wheelhouse
+        # In case this breaks at any point in time, switch to using the latest version
+        # available or any other version that is not the same as the default one set
+        # in cibuildwheel/resources/build-platforms.toml.
+      env:
+        CIBW_PLATFORM: pyodide
+        CIBW_BUILD: "cp312*"
+        CIBW_PYODIDE_VERSION: "0.27.6"
+
     - name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
       run: |
         uv run --no-sync ./bin/run_tests.py
diff --git a/action.yml b/action.yml
index 4f8ff6796..3e19ab982 100644
--- a/action.yml
+++ b/action.yml
@@ -24,11 +24,10 @@ branding:
 runs:
   using: composite
   steps:
-    # Set up the version of Python that supports pyodide
     - uses: actions/setup-python@v5
       id: python
       with:
-        python-version: "3.12"
+        python-version: "3.11 - 3.13"
         update-environment: false
 
     - id: cibw
diff --git a/bin/generate_pyodide_constraints.py b/bin/generate_pyodide_constraints.py
new file mode 100755
index 000000000..e378dd8ee
--- /dev/null
+++ b/bin/generate_pyodide_constraints.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+
+import sys
+import textwrap
+from pathlib import Path
+
+import click
+
+from cibuildwheel.extra import get_pyodide_xbuildenv_info
+
+
+@click.command()
+@click.argument(
+    "pyodide-version",
+    type=str,
+)
+@click.option(
+    "--output-file",
+    type=click.Path(),
+    default=None,
+    help="Output file to write the constraints to. If not provided, the constraints will be printed to stdout.",
+)
+def generate_pyodide_constraints(pyodide_version: str, output_file: str | None = None) -> None:
+    """
+    Generate constraints for a specific Pyodide version. The constraints are
+    generated based on the Pyodide version's xbuildenv info, which is retrieved
+    from the Pyodide repository.
+
+    These constraints should then be 'pinned' using `uv pip compile`.
+
+    Example usage:
+
+        bin/generate_pyodide_constraints.py 0.27.0
+    """
+    xbuildenv_info = get_pyodide_xbuildenv_info()
+    try:
+        pyodide_version_xbuildenv_info = xbuildenv_info["releases"][pyodide_version]
+    except KeyError as e:
+        msg = f"Pyodide version {pyodide_version} not found in xbuildenv info. Versions available: {', '.join(xbuildenv_info['releases'].keys())}"
+        raise click.BadParameter(msg) from e
+
+    pyodide_build_min_version = pyodide_version_xbuildenv_info.get("min_pyodide_build_version")
+    pyodide_build_max_version = pyodide_version_xbuildenv_info.get("max_pyodide_build_version")
+
+    pyodide_build_specifier_parts: list[str] = []
+
+    if pyodide_build_min_version:
+        pyodide_build_specifier_parts.append(f">={pyodide_build_min_version}")
+    if pyodide_build_max_version:
+        pyodide_build_specifier_parts.append(f"<={pyodide_build_max_version}")
+
+    pyodide_build_specifier = ",".join(pyodide_build_specifier_parts)
+
+    constraints_txt = textwrap.dedent(f"""
+        pip
+        build[virtualenv]
+        pyodide-build{pyodide_build_specifier}
+        click<8.2
+    """)
+
+    if output_file is None:
+        print(constraints_txt)
+    else:
+        Path(output_file).write_text(constraints_txt)
+        print(f"Constraints written to {output_file}", file=sys.stderr)
+
+
+if __name__ == "__main__":
+    generate_pyodide_constraints()
diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index 116a0aeb9..2c51597fd 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -197,6 +197,9 @@
   xbuild-tools:
     description: Binaries on the path that should be included in an isolated cross-build environment
     type: string_array
+  pyodide-version:
+    type: string
+    description: Specify the version of Pyodide to use
   repair-wheel-command:
     description: Execute a shell command to repair each built wheel.
     type: string_array
diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py
new file mode 100755
index 000000000..516920144
--- /dev/null
+++ b/bin/update_python_build_standalone.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+
+import json
+
+from cibuildwheel.extra import github_api_request
+from cibuildwheel.util.python_build_standalone import (
+    PythonBuildStandaloneAsset,
+    PythonBuildStandaloneReleaseData,
+)
+from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES
+
+
+def main() -> None:
+    """
+    This script updates the vendored list of release assets to the latest
+    version of astral-sh/python-build-standalone.
+    """
+
+    # Get the latest release tag from the GitHub API
+    latest_release = github_api_request("repos/astral-sh/python-build-standalone/releases/latest")
+    latest_tag = latest_release["tag_name"]
+
+    # Get the list of assets for the latest release
+    github_assets = github_api_request(
+        f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}"
+    )["assets"]
+
+    assets: list[PythonBuildStandaloneAsset] = []
+
+    for github_asset in github_assets:
+        name = github_asset["name"]
+        if not name.endswith("install_only.tar.gz"):
+            continue
+        url = github_asset["browser_download_url"]
+        assets.append({"name": name, "url": url})
+
+    # Write the assets to the JSON file. One day, we might need to support
+    # multiple releases, but for now, we only support the latest one
+    json_file_contents: PythonBuildStandaloneReleaseData = {
+        "releases": [
+            {
+                "tag": latest_tag,
+                "assets": assets,
+            }
+        ]
+    }
+
+    with PYTHON_BUILD_STANDALONE_RELEASES.open("w", encoding="utf-8") as f:
+        json.dump(json_file_contents, f, indent=2)
+        # Add a trailing newline, our pre-commit hook requires it
+        f.write("\n")
+
+    print(
+        f"Updated {PYTHON_BUILD_STANDALONE_RELEASES.name} with {len(assets)} assets for tag {latest_tag}"
+    )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/bin/update_pythons.py b/bin/update_pythons.py
index 75de60a64..60b99f2aa 100755
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -19,7 +19,7 @@
 from rich.logging import RichHandler
 from rich.syntax import Syntax
 
-from cibuildwheel.extra import dump_python_configurations
+from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info
 
 log = logging.getLogger("cibw")
 
@@ -57,7 +57,14 @@ class ConfigApple(TypedDict):
     url: str
 
 
-AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple
+class ConfigPyodide(TypedDict):
+    identifier: str
+    version: str
+    default_pyodide_version: str
+    node_version: str
+
+
+AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple | ConfigPyodide
 
 
 # The following set of "Versions" classes allow the initial call to the APIs to
@@ -347,6 +354,39 @@ def update_version_ios(self, identifier: str, version: Version) -> ConfigApple |
         return None
 
 
+class PyodideVersions:
+    def __init__(self) -> None:
+        xbuildenv_info = get_pyodide_xbuildenv_info()
+        self.releases = xbuildenv_info["releases"]
+
+    def update_version_pyodide(
+        self, identifier: str, version: Version, spec: Specifier, node_version: str
+    ) -> ConfigPyodide | None:
+        # get releases that match the python version
+        releases = [
+            r for r in self.releases.values() if spec.contains(Version(r["python_version"]))
+        ]
+        # sort by version, latest first
+        releases.sort(key=lambda r: Version(r["version"]), reverse=True)
+
+        if not releases:
+            msg = f"Pyodide not found for {spec}!"
+            raise ValueError(msg)
+
+        final_releases = [r for r in releases if not Version(r["version"]).is_prerelease]
+
+        # prefer a final release if available, otherwise use the latest
+        # pre-release
+        release = final_releases[0] if final_releases else releases[0]
+
+        return ConfigPyodide(
+            identifier=identifier,
+            version=str(version),
+            default_pyodide_version=release["version"],
+            node_version=node_version,
+        )
+
+
 # This is a universal interface to all the above Versions classes. Given an
 # identifier, it updates a config dict.
 
@@ -369,6 +409,8 @@ def __init__(self) -> None:
 
         self.graalpy = GraalPyVersions()
 
+        self.pyodide = PyodideVersions()
+
     def update_config(self, config: MutableMapping[str, str]) -> None:
         identifier = config["identifier"]
         version = Version(config["version"])
@@ -407,6 +449,10 @@ def update_config(self, config: MutableMapping[str, str]) -> None:
             config_update = self.windows_arm64.update_version_windows(spec)
         elif "ios" in identifier:
             config_update = self.ios_cpython.update_version_ios(identifier, version)
+        elif "pyodide" in identifier:
+            config_update = self.pyodide.update_version_pyodide(
+                identifier, version, spec, config["node_version"]
+            )
 
         assert config_update is not None, f"{identifier} not found!"
         config.update(**config_update)
@@ -445,6 +491,9 @@ def update_pythons(force: bool, level: str) -> None:
     for config in configs["ios"]["python_configurations"]:
         all_versions.update_config(config)
 
+    for config in configs["pyodide"]["python_configurations"]:
+        all_versions.update_config(config)
+
     result_toml = dump_python_configurations(configs)
 
     rich.print()  # spacer
diff --git a/cibuildwheel/extra.py b/cibuildwheel/extra.py
index 774447c33..e13df6e0b 100644
--- a/cibuildwheel/extra.py
+++ b/cibuildwheel/extra.py
@@ -2,9 +2,16 @@
 These are utilities for the `/bin` scripts, not for the `cibuildwheel` program.
 """
 
+import json
+import time
+import typing
+import urllib.error
+import urllib.request
 from collections.abc import Mapping, Sequence
 from io import StringIO
-from typing import Protocol
+from typing import Any, NotRequired, Protocol
+
+from cibuildwheel import __version__ as cibw_version
 
 __all__ = ("Printable", "dump_python_configurations")
 
@@ -30,3 +37,65 @@ def dump_python_configurations(
         output.write("\n")
     # Strip the final newline, to avoid two blank lines at the end.
     return output.getvalue()[:-1]
+
+
+def _json_request(request: urllib.request.Request, timeout: int = 30) -> dict[str, Any]:
+    with urllib.request.urlopen(request, timeout=timeout) as response:
+        return typing.cast(dict[str, Any], json.load(response))
+
+
+def github_api_request(path: str, *, max_retries: int = 3) -> dict[str, Any]:
+    """
+    Makes a GitHub API request to the given path and returns the JSON response.
+    """
+    api_url = f"https://api.github.com/{path}"
+    headers = {
+        "Accept": "application/vnd.github.v3+json",
+        "User-Agent": f"cibuildwheel/{cibw_version}",
+    }
+    request = urllib.request.Request(api_url, headers=headers)
+
+    for retry_count in range(max_retries):
+        try:
+            return _json_request(request)
+        except (urllib.error.URLError, TimeoutError) as e:
+            # pylint: disable=E1101
+            if (
+                isinstance(e, urllib.error.HTTPError)
+                and (e.code == 403 or e.code == 429)
+                and e.headers.get("x-ratelimit-remaining") == "0"
+            ):
+                reset_time = int(e.headers.get("x-ratelimit-reset", 0))
+                wait_time = max(0, reset_time - int(e.headers.get("date", 0)))
+                print(f"Github rate limit exceeded. Waiting for {wait_time} seconds.")
+                time.sleep(wait_time)
+            else:
+                print(f"Retrying GitHub API request due to error: {e}")
+
+            if retry_count == max_retries - 1:
+                print(f"GitHub API request failed (Network error: {e}). Check network connection.")
+                raise e
+
+    # Should never be reached but to keep the type checker happy
+    msg = "Unexpected execution path in github_api_request"
+    raise RuntimeError(msg)
+
+
+class PyodideXBuildEnvRelease(typing.TypedDict):
+    version: str
+    python_version: str
+    emscripten_version: str
+    min_pyodide_build_version: NotRequired[str]
+    max_pyodide_build_version: NotRequired[str]
+
+
+class PyodideXBuildEnvInfo(typing.TypedDict):
+    releases: dict[str, PyodideXBuildEnvRelease]
+
+
+def get_pyodide_xbuildenv_info() -> PyodideXBuildEnvInfo:
+    xbuildenv_info_url = (
+        "https://pyodide.github.io/pyodide/api/pyodide-cross-build-environments.json"
+    )
+    with urllib.request.urlopen(xbuildenv_info_url) as response:
+        return typing.cast(PyodideXBuildEnvInfo, json.loads(response.read().decode("utf-8")))
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 94ffcefcb..5a9c96c25 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -112,6 +112,7 @@ class BuildOptions:
     build_frontend: BuildFrontendConfig | None
     config_settings: str
     container_engine: OCIContainerEngineConfig
+    pyodide_version: str | None
 
     @property
     def package_dir(self) -> Path:
@@ -850,6 +851,8 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                 msg = f"Failed to parse container config. {e}"
                 raise errors.ConfigurationError(msg) from e
 
+            pyodide_version = self.reader.get("pyodide-version", env_plat=False)
+
             return BuildOptions(
                 globals=self.globals,
                 test_command=test_command,
@@ -871,6 +874,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                 build_frontend=build_frontend,
                 config_settings=config_settings,
                 container_engine=container_engine,
+                pyodide_version=pyodide_version or None,
             )
 
     def check_for_invalid_configuration(self, identifiers: Iterable[str]) -> None:
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index 77e3b9a81..dc6b5c6a4 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -1,13 +1,15 @@
 import functools
+import json
 import os
 import shutil
 import sys
 import tomllib
+import typing
 from collections.abc import Set
 from dataclasses import dataclass
 from pathlib import Path
 from tempfile import TemporaryDirectory
-from typing import Final
+from typing import Final, TypedDict
 
 from filelock import FileLock
 
@@ -28,8 +30,12 @@
     extract_zip,
     move_file,
 )
-from ..util.helpers import prepare_command
+from ..util.helpers import prepare_command, unwrap, unwrap_preserving_paragraphs
 from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version
+from ..util.python_build_standalone import (
+    PythonBuildStandaloneError,
+    create_python_build_standalone_environment,
+)
 from ..venv import constraint_flags, virtualenv
 
 IS_WIN: Final[bool] = sys.platform.startswith("win")
@@ -39,12 +45,23 @@
 class PythonConfiguration:
     version: str
     identifier: str
-    pyodide_version: str
-    pyodide_build_version: str
-    emscripten_version: str
+    default_pyodide_version: str
     node_version: str
 
 
+class PyodideXBuildEnvInfoVersionRange(TypedDict):
+    min: str | None
+    max: str | None
+
+
+class PyodideXBuildEnvInfo(TypedDict):
+    version: str
+    python: str
+    emscripten: str
+    pyodide_build: PyodideXBuildEnvInfoVersionRange
+    compatible: bool
+
+
 @functools.cache
 def ensure_node(major_version: str) -> Path:
     with resources.NODEJS.open("rb") as f:
@@ -77,8 +94,6 @@ def ensure_node(major_version: str) -> Path:
 
 
 def install_emscripten(tmp: Path, version: str) -> Path:
-    # We don't need to match the emsdk version to the version we install, but
-    # we do for stability
     url = f"https://github.com/emscripten-core/emsdk/archive/refs/tags/{version}.zip"
     installation_path = CIBW_CACHE_PATH / f"emsdk-{version}"
     emsdk_path = installation_path / f"emsdk-{version}/emsdk"
@@ -96,16 +111,82 @@ def install_emscripten(tmp: Path, version: str) -> Path:
     return emcc_path
 
 
+def get_all_xbuildenv_version_info(env: dict[str, str]) -> list[PyodideXBuildEnvInfo]:
+    xbuildenvs_info_str = call(
+        "pyodide",
+        "xbuildenv",
+        "search",
+        "--json",
+        "--all",
+        env=env,
+        cwd=CIBW_CACHE_PATH,
+        capture_stdout=True,
+    ).strip()
+
+    xbuildenvs_info = json.loads(xbuildenvs_info_str)
+
+    if "environments" not in xbuildenvs_info:
+        msg = f"Invalid xbuildenvs info, got {xbuildenvs_info}"
+        raise ValueError(msg)
+
+    return typing.cast(list[PyodideXBuildEnvInfo], xbuildenvs_info["environments"])
+
+
+def get_xbuildenv_version_info(
+    env: dict[str, str], version: str, pyodide_build_version: str
+) -> PyodideXBuildEnvInfo:
+    xbuildenvs_info = get_all_xbuildenv_version_info(env)
+    for xbuildenv_info in xbuildenvs_info:
+        if xbuildenv_info["version"] == version:
+            return xbuildenv_info
+
+    msg = unwrap(f"""
+        Could not find Pyodide cross-build environment version {version} in the available
+        versions as reported by pyodide-build v{pyodide_build_version}.
+        Available pyodide xbuildenv versions are:
+        {", ".join(e["version"] for e in xbuildenvs_info if e["compatible"])}
+    """)
+    raise errors.FatalError(msg)
+
+
+# The default pyodide xbuildenv version that's specified in
+# build-platforms.toml is compatible with the pyodide-build version that's
+# pinned in the bundled constraints file. But if the user changes
+# pyodide-version and/or dependency-constraints in the cibuildwheel config, we
+# need to check if the xbuildenv version is compatible with the pyodide-build
+# version.
+def validate_pyodide_build_version(
+    xbuildenv_info: PyodideXBuildEnvInfo, pyodide_build_version: str
+) -> None:
+    """
+    Validate the Pyodide version is compatible with the installed
+    pyodide-build version.
+    """
+
+    pyodide_version = xbuildenv_info["version"]
+
+    if not xbuildenv_info["compatible"]:
+        msg = unwrap_preserving_paragraphs(f"""
+            The Pyodide xbuildenv version {pyodide_version} is not compatible
+            with the pyodide-build version {pyodide_build_version}. Please use
+            the 'pyodide xbuildenv search --all' command to find a compatible
+            version.
+
+            Set the pyodide-build version using the `dependency-constraints`
+            option, or set the Pyodide xbuildenv version using the
+            `pyodide-version` option.
+        """)
+        raise errors.FatalError(msg)
+
+
 def install_xbuildenv(env: dict[str, str], pyodide_build_version: str, pyodide_version: str) -> str:
     """Install a particular Pyodide xbuildenv version and set a path to the Pyodide root."""
-    # Since pyodide-build was unvendored from Pyodide v0.27.0, the versions of pyodide-build are
-    # not guaranteed to match the versions of Pyodide or be in sync with them. Hence, we shall
-    # specify the pyodide-build version in the root path, which will set up the xbuildenv for
-    # the requested Pyodide version.
-    pyodide_root = (
-        CIBW_CACHE_PATH
-        / f".pyodide-xbuildenv-{pyodide_build_version}/{pyodide_version}/xbuildenv/pyodide-root"
-    )
+    # Since pyodide-build was unvendored from Pyodide v0.27.0, the versions of
+    # pyodide-build are uncoupled from the versions of Pyodide. So, we specify
+    # both the pyodide-build version and the Pyodide version in the temp path.
+    xbuildenv_cache_path = CIBW_CACHE_PATH / f"pyodide-build-{pyodide_build_version}"
+    pyodide_root = xbuildenv_cache_path / pyodide_version / "xbuildenv" / "pyodide-root"
+
     with FileLock(CIBW_CACHE_PATH / "xbuildenv.lock"):
         if pyodide_root.exists():
             return str(pyodide_root)
@@ -114,31 +195,36 @@ def install_xbuildenv(env: dict[str, str], pyodide_build_version: str, pyodide_v
         # PYODIDE_ROOT so copy it first.
         env = dict(env)
         env.pop("PYODIDE_ROOT", None)
+
+        # Install the xbuildenv
         call(
             "pyodide",
             "xbuildenv",
             "install",
+            "--path",
+            str(xbuildenv_cache_path),
             pyodide_version,
             env=env,
             cwd=CIBW_CACHE_PATH,
         )
-    return str(pyodide_root)
+        assert pyodide_root.exists()
 
+    return str(pyodide_root)
 
-def get_base_python(identifier: str) -> Path:
-    implementation_id = identifier.split("-")[0]
-    majorminor = implementation_id[len("cp") :]
-    version_info = (int(majorminor[0]), int(majorminor[1:]))
-    if version_info == sys.version_info[:2]:
-        return Path(sys.executable)
 
-    major_minor = ".".join(str(v) for v in version_info)
-    python_name = f"python{major_minor}"
-    which_python = shutil.which(python_name)
-    if which_python is None:
-        msg = f"CPython {major_minor} is not installed."
-        raise errors.FatalError(msg)
-    return Path(which_python)
+def get_base_python(tmp: Path, python_configuration: PythonConfiguration) -> Path:
+    try:
+        return create_python_build_standalone_environment(
+            python_version=python_configuration.version,
+            temp_dir=tmp,
+            cache_dir=CIBW_CACHE_PATH,
+        )
+    except PythonBuildStandaloneError as e:
+        msg = unwrap(f"""
+            Failed to create a Python build environment:
+            {e}
+        """)
+        raise errors.FatalError(msg) from e
 
 
 def setup_python(
@@ -146,10 +232,13 @@ def setup_python(
     python_configuration: PythonConfiguration,
     constraints_path: Path | None,
     environment: ParsedEnvironment,
+    user_pyodide_version: str | None,
 ) -> dict[str, str]:
-    base_python = get_base_python(python_configuration.identifier)
+    log.step("Installing a base python environment...")
+    base_python = get_base_python(tmp / "base", python_configuration)
 
     log.step("Setting up build environment...")
+    pyodide_version = user_pyodide_version or python_configuration.default_pyodide_version
     venv_path = tmp / "venv"
     env = virtualenv(python_configuration.version, base_python, venv_path, None, use_uv=False)
     venv_bin_path = venv_path / "bin"
@@ -201,15 +290,28 @@ def setup_python(
         env=env,
     )
 
-    log.step(f"Installing Emscripten version: {python_configuration.emscripten_version} ...")
-    emcc_path = install_emscripten(tmp, python_configuration.emscripten_version)
+    pyodide_build_version = call(
+        "python",
+        "-c",
+        "from importlib.metadata import version; print(version('pyodide-build'))",
+        env=env,
+        capture_stdout=True,
+    ).strip()
+
+    xbuildenv_info = get_xbuildenv_version_info(env, pyodide_version, pyodide_build_version)
+    validate_pyodide_build_version(
+        xbuildenv_info=xbuildenv_info,
+        pyodide_build_version=pyodide_build_version,
+    )
+
+    emscripten_version = xbuildenv_info["emscripten"]
+    log.step(f"Installing Emscripten version: {emscripten_version} ...")
+    emcc_path = install_emscripten(tmp, emscripten_version)
 
     env["PATH"] = os.pathsep.join([str(emcc_path.parent), env["PATH"]])
 
-    log.step(f"Installing Pyodide xbuildenv version: {python_configuration.pyodide_version} ...")
-    env["PYODIDE_ROOT"] = install_xbuildenv(
-        env, python_configuration.pyodide_build_version, python_configuration.pyodide_version
-    )
+    log.step(f"Installing Pyodide xbuildenv version: {pyodide_version} ...")
+    env["PYODIDE_ROOT"] = install_xbuildenv(env, pyodide_build_version, pyodide_version)
 
     return env
 
@@ -259,6 +361,7 @@ def build(options: Options, tmp_path: Path) -> None:
             log.build_start(config.identifier)
 
             identifier_tmp_dir = tmp_path / config.identifier
+
             built_wheel_dir = identifier_tmp_dir / "built_wheel"
             repaired_wheel_dir = identifier_tmp_dir / "repaired_wheel"
             identifier_tmp_dir.mkdir()
@@ -270,10 +373,11 @@ def build(options: Options, tmp_path: Path) -> None:
             )
 
             env = setup_python(
-                identifier_tmp_dir / "build",
-                config,
-                constraints_path,
-                build_options.environment,
+                tmp=identifier_tmp_dir / "build",
+                python_configuration=config,
+                constraints_path=constraints_path,
+                environment=build_options.environment,
+                user_pyodide_version=build_options.pyodide_version,
             )
             pip_version = get_pip_version(env)
             # The Pyodide command line runner mounts all directories in the host
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 973154586..2875f9f24 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -220,7 +220,7 @@ python_configurations = [
 
 [pyodide]
 python_configurations = [
-  { identifier = "cp312-pyodide_wasm32", version = "3.12", pyodide_version = "0.27.0", pyodide_build_version = "0.29.2", emscripten_version = "3.1.58", node_version = "v20" },
+  { identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.6", node_version = "v22" },
 ]
 
 [ios]
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 6a71543bf..a66b0aa77 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -424,6 +424,11 @@
       ],
       "title": "CIBW_XBUILD_TOOLS"
     },
+    "pyodide-version": {
+      "type": "string",
+      "description": "Specify the version of Pyodide to use",
+      "title": "CIBW_PYODIDE_VERSION"
+    },
     "repair-wheel-command": {
       "description": "Execute a shell command to repair each built wheel.",
       "oneOf": [
@@ -718,6 +723,9 @@
           "xbuild-tools": {
             "$ref": "#/properties/xbuild-tools"
           },
+          "pyodide-version": {
+            "$ref": "#/properties/pyodide-version"
+          },
           "repair-wheel-command": {
             "$ref": "#/properties/repair-wheel-command"
           },
@@ -830,6 +838,9 @@
         "xbuild-tools": {
           "$ref": "#/properties/xbuild-tools"
         },
+        "pyodide-version": {
+          "$ref": "#/properties/pyodide-version"
+        },
         "repair-wheel-command": {
           "description": "Execute a shell command to repair each built wheel.",
           "oneOf": [
@@ -900,6 +911,9 @@
         "xbuild-tools": {
           "$ref": "#/properties/xbuild-tools"
         },
+        "pyodide-version": {
+          "$ref": "#/properties/pyodide-version"
+        },
         "repair-wheel-command": {
           "$ref": "#/properties/repair-wheel-command"
         },
@@ -957,6 +971,9 @@
         "xbuild-tools": {
           "$ref": "#/properties/xbuild-tools"
         },
+        "pyodide-version": {
+          "$ref": "#/properties/pyodide-version"
+        },
         "repair-wheel-command": {
           "description": "Execute a shell command to repair each built wheel.",
           "oneOf": [
@@ -1027,6 +1044,9 @@
         "xbuild-tools": {
           "$ref": "#/properties/xbuild-tools"
         },
+        "pyodide-version": {
+          "$ref": "#/properties/pyodide-version"
+        },
         "repair-wheel-command": {
           "$ref": "#/properties/repair-wheel-command"
         },
@@ -1084,6 +1104,9 @@
         "xbuild-tools": {
           "$ref": "#/properties/xbuild-tools"
         },
+        "pyodide-version": {
+          "$ref": "#/properties/pyodide-version"
+        },
         "repair-wheel-command": {
           "$ref": "#/properties/repair-wheel-command"
         },
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 702ea12d2..e9116769d 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -4,7 +4,7 @@ annotated-types==0.7.0
     # via pydantic
 anyio==4.9.0
     # via httpx
-auditwheel-emscripten==0.0.16
+auditwheel-emscripten==0.1.0
     # via pyodide-build
 build==1.2.2.post1
     # via
@@ -21,8 +21,6 @@ click==8.1.8
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   typer
-cmake==4.0.2
-    # via pyodide-build
 distlib==0.3.9
     # via virtualenv
 filelock==3.18.0
@@ -53,7 +51,9 @@ packaging==25.0
 pip==25.1.1
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
 platformdirs==4.3.8
-    # via virtualenv
+    # via
+    #   pyodide-build
+    #   virtualenv
 pydantic==2.11.4
     # via
     #   pyodide-build
@@ -62,9 +62,9 @@ pydantic-core==2.33.2
     # via pydantic
 pygments==2.19.1
     # via rich
-pyodide-build==0.29.2
+pyodide-build==0.30.4
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
-pyodide-cli==0.2.4
+pyodide-cli==0.3.0
     # via
     #   auditwheel-emscripten
     #   pyodide-build
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index f0010806e..396c1d3cc 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -28,6 +28,8 @@ test-environment = {}
 
 container-engine = "docker"
 
+pyodide-version = ""
+
 manylinux-x86_64-image = "manylinux_2_28"
 manylinux-i686-image = "manylinux2014"
 manylinux-aarch64-image = "manylinux_2_28"
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
new file mode 100644
index 000000000..407b0318c
--- /dev/null
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -0,0 +1,425 @@
+{
+  "releases": [
+    {
+      "tag": "20250409",
+      "assets": [
+        {
+          "name": "cpython-3.10.17+20250409-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.17+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.11.12+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.13.3+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.14.0a6+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+        }
+      ]
+    }
+  ]
+}
diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py
index f6619dd92..310778198 100644
--- a/cibuildwheel/util/packaging.py
+++ b/cibuildwheel/util/packaging.py
@@ -165,6 +165,9 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
                     continue
                 if not tag.platform.endswith(f"_{arch}"):
                     continue
+            elif platform.startswith("pyodide"):
+                # each Pyodide version has its own platform tag
+                continue
             else:
                 # Windows should exactly match
                 if tag.platform != platform:
diff --git a/cibuildwheel/util/python_build_standalone.py b/cibuildwheel/util/python_build_standalone.py
new file mode 100644
index 000000000..95721ee54
--- /dev/null
+++ b/cibuildwheel/util/python_build_standalone.py
@@ -0,0 +1,181 @@
+import fnmatch
+import functools
+import json
+import platform
+import typing
+from pathlib import Path
+
+from filelock import FileLock
+
+from cibuildwheel.util.file import download, extract_tar
+from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES
+
+
+class PythonBuildStandaloneAsset(typing.TypedDict):
+    name: str
+    url: str
+
+
+class PythonBuildStandaloneRelease(typing.TypedDict):
+    tag: str
+    assets: list[PythonBuildStandaloneAsset]
+
+
+class PythonBuildStandaloneReleaseData(typing.TypedDict):
+    releases: list[PythonBuildStandaloneRelease]
+
+
+@functools.cache
+def get_python_build_standalone_release_data() -> PythonBuildStandaloneReleaseData:
+    with open(PYTHON_BUILD_STANDALONE_RELEASES, "rb") as f:
+        return typing.cast(PythonBuildStandaloneReleaseData, json.load(f))
+
+
+class PythonBuildStandaloneError(Exception):
+    """Errors related to python-build-standalone."""
+
+
+def _get_platform_identifiers() -> tuple[str, str, str | None]:
+    """
+    Detects the current platform and returns architecture, platform, and libc
+    identifiers.
+    """
+    system = platform.system()
+    machine = platform.machine()
+    machine_lower = machine.lower()
+
+    arch_identifier: str
+    platform_identifier: str
+    libc_identifier: str | None = None
+
+    # Map Architecture
+    if machine_lower in ["x86_64", "amd64"]:
+        arch_identifier = "x86_64"
+    elif machine_lower in ["aarch64", "arm64"]:
+        arch_identifier = "aarch64"
+    else:
+        msg = f"Unsupported architecture: {system} {machine}. Cannot download appropriate Python build."
+        raise PythonBuildStandaloneError(msg)
+
+    # Map OS + Libc
+    if system == "Linux":
+        platform_identifier = "unknown-linux"
+        libc_identifier = "musl" if "musl" in (platform.libc_ver() or ("", "")) else "gnu"
+    elif system == "Darwin":
+        platform_identifier = "apple-darwin"
+    elif system == "Windows":
+        platform_identifier = "pc-windows-msvc"
+    else:
+        msg = f"Unsupported operating system: {system}. Cannot download appropriate Python build."
+        raise PythonBuildStandaloneError(msg)
+
+    print(
+        f"Detected platform: arch='{arch_identifier}', platform='{platform_identifier}', libc='{libc_identifier}'"
+    )
+    return arch_identifier, platform_identifier, libc_identifier
+
+
+def _get_pbs_asset(
+    *,
+    python_version: str,
+    arch_identifier: str,
+    platform_identifier: str,
+    libc_identifier: str | None,
+) -> tuple[str, str, str]:
+    """Finds the asset, returning (tag, filename, url)."""
+    release_data = get_python_build_standalone_release_data()
+
+    expected_suffix = f"{arch_identifier}-{platform_identifier}"
+    if libc_identifier:
+        expected_suffix += f"-{libc_identifier}"
+    expected_suffix += "-install_only.tar.gz"
+
+    asset_pattern = f"cpython-{python_version}.*-{expected_suffix}"
+    print(f"Looking for file with pattern {asset_pattern}")
+
+    for release in release_data["releases"]:
+        for asset in release["assets"]:
+            asset_name = asset["name"]
+            if not fnmatch.fnmatch(asset_name, asset_pattern):
+                continue
+
+            asset_url = asset["url"]
+            return release["tag"], asset_url, asset_name
+
+    # If loop completes without finding a match
+    msg = f"Could not find python-build-standalone release asset matching {asset_pattern!r}."
+    raise PythonBuildStandaloneError(msg)
+
+
+def _download_or_get_from_cache(asset_url: str, asset_filename: str, cache_dir: Path) -> Path:
+    with FileLock(cache_dir / (asset_filename + ".lock")):
+        asset_cache_path = cache_dir / asset_filename
+        if asset_cache_path.is_file():
+            print(f"Using cached python_build_standalone: {asset_cache_path}")
+            return asset_cache_path
+
+        print(f"Downloading python_build_standalone: {asset_url} to {asset_cache_path}")
+        download(asset_url, asset_cache_path)
+        return asset_cache_path
+
+
+def _find_python_executable(extracted_dir: Path) -> Path:
+    """Finds the python executable within the extracted directory structure."""
+    # Structure is typically 'python/bin/python' or 'python/python.exe'
+    base_install_dir = extracted_dir / "python"
+
+    if platform.system() == "Windows":
+        executable_path = base_install_dir / "python.exe"
+    else:
+        executable_path = base_install_dir / "bin" / "python"
+
+    if not executable_path.is_file():
+        msg = f"Could not locate python executable at expected path {executable_path} within {extracted_dir}."
+        raise PythonBuildStandaloneError(msg)
+
+    print(f"Found python executable: {executable_path}")
+    return executable_path.resolve()  # Return absolute path
+
+
+def create_python_build_standalone_environment(
+    python_version: str, temp_dir: Path, cache_dir: Path
+) -> Path:
+    """
+    Returns a Python environment from python-build-standalone, downloading it
+    if necessary using a cache, and expanding it into a fresh base path.
+
+    Args:
+        python_version: The Python version string (e.g., "3.12").
+        temp_dir: A directory where the Python environment will be created.
+        cache_dir: A directory to store/retrieve downloaded archives.
+
+    Returns:
+        The absolute path to the python executable within the created environment (in temp_dir).
+
+    Raises:
+        PythonBuildStandaloneError: If the platform is unsupported, the build cannot be found,
+                    download/extraction fails, or configuration is invalid.
+    """
+
+    print(f"Creating python-build-standalone environment: version={python_version}")
+
+    arch_id, platform_id, libc_id = _get_platform_identifiers()
+
+    pbs_tag, asset_url, asset_filename = _get_pbs_asset(
+        python_version=python_version,
+        arch_identifier=arch_id,
+        platform_identifier=platform_id,
+        libc_identifier=libc_id,
+    )
+
+    print(f"Using python-build-standalone release: {pbs_tag}")
+
+    archive_path = _download_or_get_from_cache(
+        asset_url=asset_url, asset_filename=asset_filename, cache_dir=cache_dir
+    )
+
+    python_base_dir = temp_dir / "pbs"
+    assert not python_base_dir.exists()
+    extract_tar(archive_path, python_base_dir)
+
+    return _find_python_executable(python_base_dir)
diff --git a/cibuildwheel/util/resources.py b/cibuildwheel/util/resources.py
index 40e085d68..a3cef7815 100644
--- a/cibuildwheel/util/resources.py
+++ b/cibuildwheel/util/resources.py
@@ -16,6 +16,7 @@
 CONSTRAINTS: Final[Path] = PATH / "constraints.txt"
 VIRTUALENV: Final[Path] = PATH / "virtualenv.toml"
 CIBUILDWHEEL_SCHEMA: Final[Path] = PATH / "cibuildwheel.schema.json"
+PYTHON_BUILD_STANDALONE_RELEASES: Final[Path] = PATH / "python-build-standalone-releases.json"
 
 
 # this value is cached because it's used a lot in unit tests
diff --git a/docs/options.md b/docs/options.md
index 1f4d4d120..3bd90369f 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -17,7 +17,9 @@ Default: `auto`
 - For `linux`, you need [Docker or Podman](#container-engine) running, on Linux, macOS, or Windows.
 - For `macos` and `windows`, you need to be running on the respective system, with a working compiler toolchain installed - Xcode Command Line tools for macOS, and MSVC for Windows.
 - For `ios` you need to be running on macOS, with Xcode and the iOS simulator installed.
-- For `pyodide` you need to be on an x86-64 linux runner and `python3.12` must be available in `PATH`.
+- For `pyodide`, you need a Linux or macOS machine.
+
+Check the [platforms](platforms.md) page for more information on platform requirements.
 
 This option can also be set using the [command-line option](#command-line) `--platform`. This option is not available in the `pyproject.toml` config.
 
@@ -49,20 +51,19 @@ When both options are specified, both conditions are applied and only builds wit
 When setting the options, you can use shell-style globbing syntax, as per [fnmatch](https://docs.python.org/3/library/fnmatch.html) with the addition of curly bracket syntax `{option1,option2}`, provided by [bracex](https://pypi.org/project/bracex/). All the build identifiers supported by cibuildwheel are shown below:
 
 <div class="build-id-table-marker"></div>
-
-|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                                                                                   | iOS                                                                                               |
-|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
-| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-manylinux_riscv64<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l<br/>cp38-musllinux_riscv64           |                                                                                                   |
-| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-manylinux_riscv64<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l<br/>cp39-musllinux_riscv64           |                                                                                                   |
-| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 |                                                                                                   |
-| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                                                                   |
-| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                                                                   |
-| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator |
-| Python 3.14   | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 |                                                                                                   |
-| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |
-| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |
-| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |
-| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |
+|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                                                                                   | iOS                                                                                               | pyodide (WASM)       |
+|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|----------------------|
+| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-manylinux_riscv64<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l<br/>cp38-musllinux_riscv64           |                                                                                                   |                      |
+| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-manylinux_riscv64<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l<br/>cp39-musllinux_riscv64           |                                                                                                   |                      |
+| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 |                                                                                                   |                      |
+| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                                                                   |                      |
+| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                                                                   | cp312-pyodide_wasm32 |
+| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator |                      |
+| Python 3.14   | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 |                                                                                                   |                      |
+| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
+| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
+| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
+| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
 
 The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
 The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
@@ -70,9 +71,6 @@ The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425]
 Windows arm64 platform support is experimental.
 Linux riscv64 platform support is experimental and requires an explicit opt-in through [CIBW_ENABLE](#enable).
 
-For an experimental WebAssembly build with `--platform pyodide`,
-`cp312-pyodide_wasm32` is the only platform identifier.
-
 See the [cibuildwheel 2 documentation](https://cibuildwheel.pypa.io/en/2.x/) for past end-of-life versions of Python.
 
 #### Examples
@@ -1223,6 +1221,45 @@ Platform-specific environment variables are also available:<br/>
     dependency-versions = { packages = ["pyodide-build==0.29.1"] }
     ```
 
+### `CIBW_PYODIDE_VERSION` {: #pyodide-version}
+
+> Specify the Pyodide version to use for `pyodide` platform builds
+
+This option allows you to specify a specific version of Pyodide to be used when building wheels for the `pyodide` platform. If unset, cibuildwheel will use a pinned Pyodide version.
+
+This option is particularly useful for:
+
+- Testing against specific Pyodide alpha or older releases.
+- Ensuring reproducibility by targeting a known Pyodide version.
+
+The available Pyodide versions are determined by the version of `pyodide-build` being used. You can list the compatible versions using the command `pyodide xbuildenv search --all` as described in the [Pyodide platform documentation](platforms.md#pyodide-choosing-a-version).
+
+!!! tip
+    You can set the version of `pyodide-build` using the [`CIBW_DEPENDENCY_VERSIONS`](#dependency-versions) option.
+
+#### Examples
+
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Build Pyodide wheels using Pyodide version 0.27.6
+    CIBW_PYODIDE_VERSION: 0.27.6
+
+    # Build Pyodide wheels using a specific alpha release
+    CIBW_PYODIDE_VERSION: 0.28.0a2
+    ```
+
+!!! tab examples "pyproject.toml"
+
+    ```toml
+    [tool.cibuildwheel.pyodide]
+    # Build Pyodide wheels using Pyodide version 0.27.6
+    pyodide-version = "0.27.6"
+
+    [tool.cibuildwheel.pyodide]
+    # Build Pyodide wheels using a specific alpha release
+    pyodide-version = "0.28.0a2"
+    ```
 
 ## Testing
 
diff --git a/docs/platforms.md b/docs/platforms.md
index 115e91455..c4ffb9af6 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -3,7 +3,7 @@ title: Platforms
 ---
 # Platforms
 
-## Linux
+## Linux {: #linux}
 
 ### System requirements
 
@@ -31,7 +31,7 @@ Linux wheels are built in [`manylinux`/`musllinux` containers](https://github.co
 
 -   Alternative Docker images can be specified with the `CIBW_MANYLINUX_*_IMAGE`/`CIBW_MUSLLINUX_*_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#linux-image) for more details.
 
-## macOS
+## macOS {: #macos}
 
 ### System requirements
 
@@ -138,7 +138,7 @@ Regarding testing,
     As a workaround, the tag can be fixed before running delocate to repair the wheel. The [`wheel tags`](https://wheel.readthedocs.io/en/stable/reference/wheel_tags.html) command is ideal for this. See [this workflow](https://gist.github.com/anderssonjohan/49f07e33fc5cb2420515a8ac76dc0c95#file-build-pendulum-wheels-yml-L39-L53) for an example usage of `wheel tags`.
 
 
-## Windows
+## Windows {: #windows}
 
 ### System requirements
 
@@ -160,15 +160,23 @@ By default, `ARM64` is not enabled when running on non-ARM64 runners. Use [`CIBW
 
 Pyodide is offered as an experimental feature in cibuildwheel.
 
-### Prerequisites
+### System requirements
 
-You need to have a matching host version of Python (unlike all other cibuildwheel platforms). Linux host highly recommended; macOS hosts may work (e.g. invoking `pytest` directly in [`CIBW_TEST_COMMAND`](options.md#test-command) is [currently failing](https://github.com/pyodide/pyodide/issues/4802)) and Windows hosts will not work.
+Pyodide builds require a Linux or macOS machine.
 
 ### Specifying a pyodide build
 
 You must target pyodide with `--platform pyodide` (or use `--only` on the identifier).
 
-## iOS
+### Choosing a Pyodide version {: #pyodide-choosing-a-version}
+
+It is also possible to target a specific Pyodide version by setting the `CIBW_PYODIDE_VERSION` option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
+
+### Running tests
+
+Currently, it's recommended to run tests using a `python -m` entrypoint, rather than a command line entrypoint, or a shell script. This is because custom entrypoints have some issues in the Pyodide virtual environment. For example, `pytest` may not work as a command line entrypoint, but will work as a `python -m pytest` entrypoint.
+
+## iOS {: #ios}
 
 ### System requirements
 
diff --git a/noxfile.py b/noxfile.py
index ba3ce2ee8..a31c147f6 100755
--- a/noxfile.py
+++ b/noxfile.py
@@ -68,17 +68,20 @@ def update_constraints(session: nox.Session) -> None:
     Update the dependencies inplace.
     """
 
+    session.install("-e.", "click")
+
     resources = Path("cibuildwheel/resources")
 
     if session.venv_backend != "uv":
         session.install("uv>=0.1.23")
 
+    # CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
+    # regenerate the constraints files
+    env = os.environ.copy()
+    env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}"
+
     for minor_version in range(8, 15):
         python_version = f"3.{minor_version}"
-        env = os.environ.copy()
-        # CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
-        # regenerate the constraints files
-        env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}"
         output_file = resources / f"constraints-python{python_version.replace('.', '')}.txt"
         session.run(
             "uv",
@@ -100,12 +103,19 @@ def update_constraints(session: nox.Session) -> None:
     pyodides = build_platforms["pyodide"]["python_configurations"]
     for pyodide in pyodides:
         python_version = ".".join(pyodide["version"].split(".")[:2])
-        pyodide_build_version = pyodide["pyodide_build_version"]
-        output_file = resources / f"constraints-pyodide{python_version.replace('.', '')}.txt"
+        pyodide_version = pyodide["default_pyodide_version"]
+
         tmp_file = Path(session.create_tmp()) / "constraints-pyodide.in"
-        tmp_file.write_text(
-            f"pip\nbuild[virtualenv]\npyodide-build=={pyodide_build_version}\nclick<8.2"
+
+        session.run(
+            "python",
+            "bin/generate_pyodide_constraints.py",
+            "--output-file",
+            tmp_file,
+            pyodide_version,
         )
+
+        output_file = resources / f"constraints-pyodide{python_version.replace('.', '')}.txt"
         session.run(
             "uv",
             "pip",
@@ -121,7 +131,8 @@ def update_constraints(session: nox.Session) -> None:
 @nox.session(default=False, tags=["update"])
 def update_pins(session: nox.Session) -> None:
     """
-    Update the python, docker and virtualenv pins version inplace.
+    Update the python, docker, virtualenv, node, and python-build-standalone
+    version pins inplace.
     """
     pyproject = nox.project.load_toml()
     session.install("-e.", *nox.project.dependency_groups(pyproject, "bin"))
@@ -129,6 +140,7 @@ def update_pins(session: nox.Session) -> None:
     session.run("python", "bin/update_docker.py")
     session.run("python", "bin/update_virtualenv.py", "--force")
     session.run("python", "bin/update_nodejs.py", "--force")
+    session.run("python", "bin/update_python_build_standalone.py")
 
 
 @nox.session(default=False, reuse_venv=True, tags=["update"])
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
index 780f6d908..d3734ae64 100644
--- a/test/test_abi_variants.py
+++ b/test/test_abi_variants.py
@@ -48,8 +48,13 @@ def test_abi3(tmp_path):
 
     # check that the expected wheels are produced
     if utils.get_platform() == "pyodide":
-        # there's only 1 possible configuration for pyodide, cp312
-        expected_wheels = utils.expected_wheels("spam", "0.1.0", python_abi_tags=["cp310-abi3"])
+        # there's only 1 possible configuration for pyodide, cp312. It builds
+        # a wheel that is tagged abi3, compatible back to 3.10
+        expected_wheels = utils.expected_wheels(
+            "spam",
+            "0.1.0",
+            python_abi_tags=["cp310-abi3"],
+        )
     else:
         expected_wheels = utils.expected_wheels(
             "spam",
@@ -189,15 +194,17 @@ def test_abi_none(tmp_path, capfd):
         },
     )
 
-    # check that the expected wheels are produced
     expected_wheels = utils.expected_wheels("ctypesexample", "1.0.0", python_abi_tags=["py3-none"])
+    # check that the expected wheels are produced
     assert set(actual_wheels) == set(expected_wheels)
 
-    # check that each wheel was built once, and reused
     captured = capfd.readouterr()
-    assert "Building wheel..." in captured.out
+
     if utils.get_platform() == "pyodide":
-        # there's only 1 possible configuration for pyodide, we won't see the message expected on following builds
+        # pyodide builds a different platform tag for each python version, so
+        # wheels are not reused
         assert "Found previously built wheel" not in captured.out
     else:
+        # check that each wheel was built once, and reused
+        assert "Building wheel..." in captured.out
         assert "Found previously built wheel" in captured.out
diff --git a/test/test_custom_repair_wheel.py b/test/test_custom_repair_wheel.py
index bd22d6343..5af012653 100644
--- a/test/test_custom_repair_wheel.py
+++ b/test/test_custom_repair_wheel.py
@@ -16,6 +16,11 @@
 wheel = Path(sys.argv[1])
 dest_dir = Path(sys.argv[2])
 platform = wheel.stem.split("-")[-1]
+if platform.startswith("pyodide"):
+    # for the sake of this test, munge the pyodide platforms into one, it's
+    # not valid, but it does activate the uniqueness check
+    platform = "pyodide"
+
 name = f"spam-0.1.0-py2-none-{platform}.whl"
 dest = dest_dir / name
 dest_dir.mkdir(parents=True, exist_ok=True)
@@ -48,11 +53,6 @@ def test(tmp_path, capfd):
         assert "Build failed because a wheel named" in captured.err
         assert exc_info.value.returncode == 6
     else:
-        # We only produced one wheel (currently Pyodide)
+        # We only produced one wheel (perhaps Pyodide)
         # check that it has the right name
-        #
-        # As far as I can tell, this is the only full test coverage for
-        # CIBW_REPAIR_WHEEL_COMMAND so this is useful even in the case when no
-        # error is raised
-        assert "spam-0.1.0-py2-none-pyodide" in captured.out
         assert result[0].startswith("spam-0.1.0-py2-none-")
diff --git a/test/test_dependency_versions.py b/test/test_dependency_versions.py
index 6e29adef1..5aa84a506 100644
--- a/test/test_dependency_versions.py
+++ b/test/test_dependency_versions.py
@@ -70,7 +70,10 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv):
     version_no_dot = python_version.replace(".", "")
     build_environment = {}
     build_pattern = f"[cp]p{version_no_dot}-*"
-    constraint_filename = f"constraints-python{version_no_dot}.txt"
+    if utils.get_platform() == "pyodide":
+        constraint_filename = f"constraints-pyodide{version_no_dot}.txt"
+    else:
+        constraint_filename = f"constraints-python{version_no_dot}.txt"
     constraint_file = resources.PATH / constraint_filename
     constraint_versions = get_versions_from_constraint_file(constraint_file)
 
diff --git a/test/test_environment.py b/test/test_environment.py
index ee50155b5..30bab81f5 100644
--- a/test/test_environment.py
+++ b/test/test_environment.py
@@ -100,7 +100,12 @@ def test_overridden_path(tmp_path, capfd):
     "build_frontend",
     [
         pytest.param("pip", marks=utils.skip_if_pyodide("No pip for pyodide")),
-        "build",
+        pytest.param(
+            "build",
+            marks=utils.skip_if_pyodide(
+                "pyodide doesn't support multiple values for PIP_CONSTRAINT"
+            ),
+        ),
     ],
 )
 def test_overridden_pip_constraint(tmp_path, build_frontend):
diff --git a/test/test_pep518.py b/test/test_pep518.py
index d519cc297..702e7f185 100644
--- a/test/test_pep518.py
+++ b/test/test_pep518.py
@@ -60,12 +60,19 @@ def test_pep518(tmp_path, build_frontend_env):
     assert not (project_dir / "42").exists()
     assert not (project_dir / "4.1.2").exists()
 
-    # pypa/build creates a "build" folder & a "*.egg-info" folder for the wheel being built,
-    # this should be harmless so remove them
+    # pypa/build creates a "build" folder & a "*.egg-info" folder for the
+    # wheel being built, this should be harmless so remove them. pyodide-build
+    # creates a ".pyodide_build" folder, but this is gitignored with a
+    # .gitignore file inside.
     contents = [
         item
         for item in project_dir.iterdir()
-        if item.name != "build" and not item.name.endswith(".egg-info")
+        if item.name != "build"
+        and not item.name.endswith(".egg-info")
+        and item.name != ".pyodide_build"
     ]
 
+    print("Project contents after build:")
+    print("\n".join(f"  {f}" for f in contents))
+
     assert len(contents) == len(basic_project.files)
diff --git a/test/test_emscripten.py b/test/test_pyodide.py
similarity index 52%
rename from test/test_emscripten.py
rename to test/test_pyodide.py
index 897cd67f1..e1dbbd56e 100644
--- a/test/test_emscripten.py
+++ b/test/test_pyodide.py
@@ -1,10 +1,10 @@
-import shutil
+import contextlib
+import subprocess
 import sys
 import textwrap
 
 import pytest
 
-from cibuildwheel.ci import CIProvider, detect_ci_provider
 from cibuildwheel.util.file import CIBW_CACHE_PATH
 
 from . import test_projects, utils
@@ -43,13 +43,7 @@ def check_node():
 @pytest.mark.parametrize("use_pyproject_toml", [True, False])
 def test_pyodide_build(tmp_path, use_pyproject_toml):
     if sys.platform == "win32":
-        pytest.skip("emsdk doesn't work correctly on Windows")
-
-    if not shutil.which("python3.12"):
-        pytest.skip("Python 3.12 not installed")
-
-    if detect_ci_provider() == CIProvider.travis_ci:
-        pytest.skip("Python 3.12 is just a non-working pyenv shim")
+        pytest.skip("pyodide-build doesn't work correctly on Windows")
 
     if use_pyproject_toml:
         basic_project.files["pyproject.toml"] = textwrap.dedent(
@@ -84,3 +78,64 @@ def test_pyodide_build(tmp_path, use_pyproject_toml):
     print("expected_wheels", expected_wheels)
 
     assert set(actual_wheels) == set(expected_wheels)
+
+
+def test_pyodide_version_incompatible(tmp_path, capfd):
+    if sys.platform == "win32":
+        pytest.skip("pyodide-build doesn't work correctly on Windows")
+
+    basic_project.generate(tmp_path)
+
+    with pytest.raises(subprocess.CalledProcessError):
+        utils.cibuildwheel_run(
+            tmp_path,
+            add_args=["--platform", "pyodide"],
+            add_env={
+                "CIBW_DEPENDENCY_VERSIONS": "packages: pyodide-build==0.29.3",
+                "CIBW_PYODIDE_VERSION": "0.26.0a6",
+            },
+        )
+
+    out, err = capfd.readouterr()
+
+    assert "is not compatible with the pyodide-build version" in err
+
+
+@pytest.mark.parametrize("expect_failure", [True, False])
+def test_pyodide_build_and_test(tmp_path, expect_failure):
+    if sys.platform == "win32":
+        pytest.skip("pyodide-build doesn't work correctly on Windows")
+
+    if expect_failure:
+        basic_project.files["test/spam_test.py"] = textwrap.dedent(r"""
+            def test_filter():
+                assert 0 == 1
+        """)
+    else:
+        basic_project.files["test/spam_test.py"] = textwrap.dedent(r"""
+            import spam
+            def test_filter():
+                assert spam.filter("spam") == 0
+        """)
+    basic_project.generate(tmp_path)
+
+    context = (
+        pytest.raises(subprocess.CalledProcessError) if expect_failure else contextlib.nullcontext()
+    )
+    with context:
+        # build the wheels
+        actual_wheels = utils.cibuildwheel_run(
+            tmp_path,
+            add_args=["--platform", "pyodide"],
+            add_env={
+                "CIBW_TEST_REQUIRES": "pytest",
+                "CIBW_TEST_COMMAND": "python -m pytest",
+            },
+        )
+        # check that the expected wheels are produced
+        expected_wheels = [
+            "spam-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl",
+        ]
+        print("actual_wheels", actual_wheels)
+        print("expected_wheels", expected_wheels)
+        assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/test_testing.py b/test/test_testing.py
index 5caa35e42..88054c545 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -224,10 +224,10 @@ def test_test_sources(tmp_path):
         project_dir,
         add_env={
             "CIBW_TEST_REQUIRES": "pytest",
-            "CIBW_TEST_COMMAND": "pytest",
             # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
             # until https://github.com/oracle/graalpython/issues/490 is fixed
             "CIBW_TEST_COMMAND_WINDOWS": "where graalpy || pytest",
+            "CIBW_TEST_COMMAND": utils.invoke_pytest(),
             "CIBW_TEST_SOURCES": "test",
         },
     )
diff --git a/test/utils.py b/test/utils.py
index 028e88863..55b492a81 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -382,7 +382,15 @@ def _expected_wheels(
                 raise Exception(msg)
 
         elif platform == "pyodide":
-            platform_tags = ["pyodide_2024_0_wasm32"]
+            platform_tags = {
+                "cp312-cp312": ["pyodide_2024_0_wasm32"],
+                "cp313-cp313": ["pyodide_2025_0_wasm32"],
+            }.get(python_abi_tag, [])
+
+            if not platform_tags:
+                # for example if the python tag is `none` or `abi3`, all
+                # platform tags are built with that python tag
+                platform_tags = ["pyodide_2024_0_wasm32"]
 
         else:
             msg = f"Unsupported platform {platform!r}"
@@ -425,7 +433,7 @@ def skip_if_pyodide(reason: str) -> Any:
 
 def invoke_pytest() -> str:
     # see https://github.com/pyodide/pyodide/issues/4802
-    if get_platform() == "pyodide" and sys.platform.startswith("darwin"):
+    if get_platform() == "pyodide":
         return "python -m pytest"
     return "pytest"
 
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index fbf181754..9e5a1f089 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -34,6 +34,8 @@
 
 environment-pass = ["EXAMPLE_ENV"]
 
+pyodide-version = "0.27.6"
+
 [tool.cibuildwheel.macos]
 test-requires = "else"
 
@@ -88,6 +90,9 @@ def test_options_1(tmp_path, monkeypatch):
     assert local.test_sources == ["test", "other dir"]
     assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux_2_34"]
 
+    local = options.build_options("cp312-pyodide_wasm32")
+    assert local.pyodide_version == "0.27.6"
+
 
 def test_passthrough(tmp_path, monkeypatch):
     with tmp_path.joinpath("pyproject.toml").open("w") as f:
diff --git a/unit_test/utils_test.py b/unit_test/utils_test.py
index 3c4a58859..8b0be33d3 100644
--- a/unit_test/utils_test.py
+++ b/unit_test/utils_test.py
@@ -12,6 +12,8 @@
     format_safe,
     parse_key_value_string,
     prepare_command,
+    unwrap,
+    unwrap_preserving_paragraphs,
 )
 from cibuildwheel.util.packaging import find_compatible_wheel
 
@@ -363,3 +365,33 @@ def test_copy_test_sources_alternate_copy_into(sample_project):
         ],
         any_order=True,
     )
+
+
+def test_unwrap():
+    assert (
+        unwrap("""
+            This is a
+            multiline
+            string
+        """)
+        == "This is a multiline string"
+    )
+
+
+def test_unwrap_preserving_paragraphs():
+    assert (
+        unwrap("""
+            This is a
+            multiline
+            string
+        """)
+        == "This is a multiline string"
+    )
+    assert (
+        unwrap_preserving_paragraphs("""
+            paragraph one
+
+            paragraph two
+        """)
+        == "paragraph one\n\nparagraph two"
+    )

From 327f5c327dcf37610148cf8e1e93a9a086c0da2c Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 22 May 2025 06:01:58 -0400
Subject: [PATCH 078/193] ci: add labels to autoupdate (#2408)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .github/workflows/update-dependencies.yml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml
index c22469df6..2f856d47e 100644
--- a/.github/workflows/update-dependencies.yml
+++ b/.github/workflows/update-dependencies.yml
@@ -55,3 +55,7 @@ jobs:
         sign-commits: true
         token: ${{ steps.generate-token.outputs.token }}
         delete-branch: true
+        labels: |
+          CI: GraalPy
+          CI: PyPy
+          dependencies

From 1bfec0e7c948aa3901e1cdf127b0a31b9edc1212 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sun, 25 May 2025 07:58:13 -0400
Subject: [PATCH 079/193] chore: move codespell config to pyproject.toml
 (#2409)

* chore: move codespell config to pyproject.toml

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml |  2 +-
 pyproject.toml          | 11 +++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2705e71a6..2304b31fe 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -75,7 +75,7 @@ repos:
   rev: v2.4.1
   hooks:
   - id: codespell
-    args: ["-L", "sur,assertin,hep", "-w"]
+    args: ["-w"]
     exclude: ^docs/working-examples\.md$  # Autogenerated
 
 
diff --git a/pyproject.toml b/pyproject.toml
index cb5ae27a6..601e384ef 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -231,3 +231,14 @@ ignore = ["PC170", "PP303"]
 
 [tool.check-wheel-contents]
 ignore = ["W002"]  # constraints-*.txt are allowed to be duplicates of one another
+
+[tool.codespell]
+ignore-words-list = [
+    "sur",
+    "assertin",
+]
+skip = [
+    '^docs/working-examples\.md',
+    'htmlcov',
+    'all_known_setup.yaml',
+]

From dded469d60a95bc989b3dc6bd965103d9227e2fa Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sun, 25 May 2025 13:02:14 +0100
Subject: [PATCH 080/193] Bump version: v3.0.0b2

---
 README.md                              | 25 ++++++++++++++++---------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      | 16 +++++++++++++++-
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 56 insertions(+), 35 deletions(-)

diff --git a/README.md b/README.md
index 7f609295b..2de5577d5 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0b1
+        run: python -m pip install cibuildwheel==3.0.0b2
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -223,6 +223,16 @@ Not yet released, but available for testing.
 
 Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
+Known issues:
+- ⚠️ The CWD for test-command has changed in v3.0, but that is still [being debated](https://github.com/pypa/cibuildwheel/issues/2406), it might change before the final release. Please provide feedback on the aforementioned issue if you do (or don't!) encounter issues with this.
+
+#### v3.0.0b2
+
+_25 May 2025_
+
+- ✨ Adds the [`CIBW_TEST_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/latest/options/#test-environment) option, which allows you to set environment variables for the test command. cibuildwheel now sets `PYTHONSAFEPATH=1` in test environments by default, to avoid picking up package imports from the local directory - we want to test the installed wheel, not the source tree! You can change that, or any other environment variable in the test environment using this option. (#2388)
+- ✨ Improves support for Pyodide builds and adds the [`CIBW_PYODIDE_VERSION`](https://cibuildwheel.pypa.io/en/latest/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
+
 #### v3.0.0b1
 
 _19 May 2025_
@@ -232,7 +242,11 @@ _19 May 2025_
 - ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b1.
 
     _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
-- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources). \[discussion about the test cwd change and how to use to come!\]
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources), and changes the working directory for tests.
+
+    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into a temporary directory, and run the tests from there. This is required for iOS builds, but also useful for other platforms, as it allows you to test the installed wheel without any chance of accidentally importing from the source tree.
+    - If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).
+
 - ✨ Added `dependency-versions` inline syntax (#2123)
 - 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
 - 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
@@ -254,13 +268,6 @@ _24 March 2025_
 - 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
 -
 
-### v2.23.1
-
-_15 March 2025_
-
-- ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
-- 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
-
 <!-- END bin/update_readme_changelog.py -->
 
 ---
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 3996ce876..c9e418604 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0b1"
+__version__ = "3.0.0b2"
diff --git a/docs/changelog.md b/docs/changelog.md
index 0ae1f7d1f..740f82b41 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -10,6 +10,16 @@ Not yet released, but available for testing.
 
 Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
+Known issues:
+- ⚠️ The CWD for test-command has changed in v3.0, but that is still [being debated](https://github.com/pypa/cibuildwheel/issues/2406), it might change before the final release. Please provide feedback on the aforementioned issue if you do (or don't!) encounter issues with this.
+
+#### v3.0.0b2
+
+_25 May 2025_
+
+- ✨ Adds the [`CIBW_TEST_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/latest/options/#test-environment) option, which allows you to set environment variables for the test command. cibuildwheel now sets `PYTHONSAFEPATH=1` in test environments by default, to avoid picking up package imports from the local directory - we want to test the installed wheel, not the source tree! You can change that, or any other environment variable in the test environment using this option. (#2388)
+- ✨ Improves support for Pyodide builds and adds the [`CIBW_PYODIDE_VERSION`](https://cibuildwheel.pypa.io/en/latest/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
+
 #### v3.0.0b1
 
 _19 May 2025_
@@ -19,7 +29,11 @@ _19 May 2025_
 - ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b1.
 
     _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
-- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources). \[discussion about the test cwd change and how to use to come!\]
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources), and changes the working directory for tests.
+
+    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into a temporary directory, and run the tests from there. This is required for iOS builds, but also useful for other platforms, as it allows you to test the installed wheel without any chance of accidentally importing from the source tree.
+    - If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).
+
 - ✨ Added `dependency-versions` inline syntax (#2123)
 - 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
 - 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
diff --git a/docs/faq.md b/docs/faq.md
index 44c914786..42e38c4bb 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0b1
+uses: pypa/cibuildwheel@v3.0.0b2
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0b1
+cibuildwheel==3.0.0b2
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0b1 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0b2 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index fa727b9a9..de5fee7a1 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0b1
+        pip3 install cibuildwheel==3.0.0b2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0b1
+        python3 -m pip install cibuildwheel==3.0.0b2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0b1
+        pip install cibuildwheel==3.0.0b2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 21a8b5dfa..45bd7ac90 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b1
+            python3 -m pip install --user cibuildwheel==3.0.0b2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b1
+            python3 -m pip install --user cibuildwheel==3.0.0b2
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0b1
+            pip3 install cibuildwheel==3.0.0b2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index dfc75146d..ddbfac6ff 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b1
+    - python -m pip install cibuildwheel==3.0.0b2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index f601f3f30..bc6a655aa 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b1
+    - python -m pip install cibuildwheel==3.0.0b2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 4888f19e9..da63c0922 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b1
+        uses: pypa/cibuildwheel@v3.0.0b2
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index a0102bb0f..838d5b395 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b1
+        uses: pypa/cibuildwheel@v3.0.0b2
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index 14775233a..166c6162a 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0b1
+        run: pipx run cibuildwheel==3.0.0b2
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index c0325f9b8..1db755101 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b1
+        uses: pypa/cibuildwheel@v3.0.0b2
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 40ef7e3d5..420dce8cb 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0b1
+    - python -m pip install cibuildwheel==3.0.0b2
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0b1
+    - py -m pip install cibuildwheel==3.0.0b2
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0b1
+    - python3 -m pip install cibuildwheel==3.0.0b2
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index abb1b682d..937cd8f35 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0b1
+    - python -m pip install cibuildwheel==3.0.0b2
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index 7a307e706..976b55323 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b1
+  - python3 -m pip install cibuildwheel==3.0.0b2
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 8b6077435..d8018a40b 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b1
+  - python3 -m pip install cibuildwheel==3.0.0b2
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 9f3176575..3b6c211e0 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0b1 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0b1 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index cb5ae27a6..d2b8cf705 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0b1"
+version = "3.0.0b2"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From e2b64318229bced9568d6b5f7b9c11ba0a702c02 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Mon, 26 May 2025 00:35:27 -0400
Subject: [PATCH 081/193] docs: flip TOML and envvars order (#2389)

---
 docs/extra.css  |   36 ++
 docs/options.md | 1071 ++++++++++++++++++++++++++---------------------
 docs/setup.md   |   36 +-
 3 files changed, 658 insertions(+), 485 deletions(-)

diff --git a/docs/extra.css b/docs/extra.css
index df7103cea..ecd99f667 100644
--- a/docs/extra.css
+++ b/docs/extra.css
@@ -45,6 +45,42 @@ h1, h2, h3, h4, h5, h6 {
     border-bottom: 1px solid  #e1e4e5;
     margin-bottom: 0.8em;
     padding-bottom: 0.2em;
+    scroll-margin: 1.5em 0;
+}
+
+/* make it so tables can overflow the content area, make the most of the content area */
+.wy-nav-content-wrap {
+  container-type: inline-size;
+}
+.wy-table-responsive {
+  overflow-x: auto;
+  overflow-y: hidden;
+  position: relative;
+  left: -3.236em;
+  max-width: calc(100cqw);
+  width: fit-content;
+  min-width: calc(100% + 3.236em + 3.236em);
+}
+.rst-content .section .wy-table-responsive .docutils {
+  padding-left: 3.236em;
+  padding-right: 3.236em;
+}
+.rst-content .section .wy-table-responsive .docutils thead,
+.rst-content .section .wy-table-responsive .docutils tbody {
+  background-color: white;
+}
+@media screen and (max-width: 768px) {
+  .wy-table-responsive {
+    left: -1em;
+  }
+  .rst-content .section .wy-table-responsive .docutils {
+    padding-left: 1em;
+    padding-right: 1em;
+  }
+}
+
+.wy-nav-content {
+  max-width: 900px;
 }
 
 .rst-content blockquote {
diff --git a/docs/options.md b/docs/options.md
index 3bd90369f..3ade43d93 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -4,7 +4,7 @@
 
 ## Build selection
 
-### `CIBW_PLATFORM` {: #platform}
+### `platform` {: #platform cmd-line env-var }
 
 > Override the auto-detected target platform
 
@@ -40,13 +40,13 @@ This option can also be set using the [command-line option](#command-line) `--pl
     not require `--platform` or `--arch`, and will override any build/skip
     configuration.
 
-### `CIBW_BUILD`, `CIBW_SKIP` {: #build-skip}
+### `build`, `skip` {: #build-skip toml env-var }
 
 > Choose the Python versions to build
 
 List of builds to build and skip. Each build has an identifier like `cp38-manylinux_x86_64` or `cp37-macosx_x86_64` - you can list specific ones to build and cibuildwheel will only build those, and/or list ones to skip and cibuildwheel won't try to build them.
 
-When both options are specified, both conditions are applied and only builds with a tag that matches `CIBW_BUILD` and does not match `CIBW_SKIP` will be built.
+When both options are specified, both conditions are applied and only builds with a tag that matches `build` and does not match `skip` will be built.
 
 When setting the options, you can use shell-style globbing syntax, as per [fnmatch](https://docs.python.org/3/library/fnmatch.html) with the addition of curly bracket syntax `{option1,option2}`, provided by [bracex](https://pypi.org/project/bracex/). All the build identifiers supported by cibuildwheel are shown below:
 
@@ -64,99 +64,107 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
 | PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
 | PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
 | PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
+| GraalPy v24.2 | gp242-macosx_x86_64<br/>gp242-macosx_arm64                             | gp242-win_amd64                                     | gp242-manylinux_x86_64                                                                              | gp242-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
 
 The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
 The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
 
 Windows arm64 platform support is experimental.
-Linux riscv64 platform support is experimental and requires an explicit opt-in through [CIBW_ENABLE](#enable).
+Linux riscv64 platform support is experimental and requires an explicit opt-in through [`enable`](#enable).
 
 See the [cibuildwheel 2 documentation](https://cibuildwheel.pypa.io/en/2.x/) for past end-of-life versions of Python.
 
 #### Examples
 
-!!! tab examples "Environment variables"
+!!! tab examples "pyproject.toml"
 
-    ```yaml
+    ```toml
+    [tool.cibuildwheel]
     # Only build on CPython 3.8
-    CIBW_BUILD: cp38-*
+    build = "cp38-*"
 
     # Skip building on CPython 3.8 on the Mac
-    CIBW_SKIP: cp38-macosx_x86_64
+    skip = "cp38-macosx_x86_64"
 
     # Skip building on CPython 3.8 on all platforms
-    CIBW_SKIP: cp38-*
+    skip = "cp38-*"
 
     # Skip CPython 3.8 on Windows
-    CIBW_SKIP: cp38-win*
+    skip = "cp38-win*"
 
     # Skip CPython 3.8 on 32-bit Windows
-    CIBW_SKIP: cp38-win32
+    skip = "cp38-win32"
 
     # Skip CPython 3.8 and CPython 3.9
-    CIBW_SKIP: cp38-* cp39-*
+    skip = ["cp38-*", "cp39-*"]
 
     # Skip Python 3.8 on Linux
-    CIBW_SKIP: cp38-manylinux*
+    skip = "cp38-manylinux*"
 
     # Skip 32-bit builds
-    CIBW_SKIP: "*-win32 *-manylinux_i686"
+    skip = ["*-win32", "*-manylinux_i686"]
 
     # Disable building PyPy wheels on all platforms
-    CIBW_SKIP: pp*
+    skip = "pp*"
     ```
 
-    Separate multiple selectors with a space.
-
-!!! tab examples "pyproject.toml"
+!!! tab examples "Environment variables"
 
-    ```toml
-    [tool.cibuildwheel]
+    ```yaml
     # Only build on CPython 3.8
-    build = "cp38-*"
+    CIBW_BUILD: cp38-*
 
     # Skip building on CPython 3.8 on the Mac
-    skip = "cp38-macosx_x86_64"
+    CIBW_SKIP: cp38-macosx_x86_64
 
     # Skip building on CPython 3.8 on all platforms
-    skip = "cp38-*"
+    CIBW_SKIP: cp38-*
 
     # Skip CPython 3.8 on Windows
-    skip = "cp38-win*"
+    CIBW_SKIP: cp38-win*
 
     # Skip CPython 3.8 on 32-bit Windows
-    skip = "cp38-win32"
+    CIBW_SKIP: cp38-win32
 
     # Skip CPython 3.8 and CPython 3.9
-    skip = ["cp38-*", "cp39-*"]
+    CIBW_SKIP: cp38-* cp39-*
 
     # Skip Python 3.8 on Linux
-    skip = "cp38-manylinux*"
+    CIBW_SKIP: cp38-manylinux*
 
     # Skip 32-bit builds
-    skip = ["*-win32", "*-manylinux_i686"]
+    CIBW_SKIP: "*-win32 *-manylinux_i686"
 
     # Disable building PyPy wheels on all platforms
-    skip = "pp*"
+    CIBW_SKIP: pp*
     ```
 
+    Separate multiple selectors with a space.
+
+
+
     It is generally recommended to set `CIBW_BUILD` as an environment variable, though `skip`
     tends to be useful in a config file; you can statically declare that you don't
-    support PyPy, for example.
+    support a specific build, for example.
 
 <style>
-  .build-id-table-marker + table {
+  .build-id-table-marker + .wy-table-responsive table {
     font-size: 90%;
-    white-space: nowrap;
   }
-  .rst-content .build-id-table-marker + table td,
-  .rst-content .build-id-table-marker + table th {
-    padding: 4px 4px;
+  .rst-content .build-id-table-marker + .wy-table-responsive table td,
+  .rst-content .build-id-table-marker + .wy-table-responsive table th {
+    padding: 4px 8px;
+    white-space: nowrap;
+    background-color: white;
   }
-  .build-id-table-marker + table td:not(:first-child) {
+  .build-id-table-marker + .wy-table-responsive table td:not(:first-child) {
     font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
     font-size: 85%;
   }
+  .build-id-table-marker + .wy-table-responsive table td:first-child,
+  .build-id-table-marker + .wy-table-responsive table th:first-child {
+    font-weight: bold;
+  }
   dt code {
     font-size: 100%;
     background-color: rgba(41, 128, 185, 0.1);
@@ -165,7 +173,7 @@ See the [cibuildwheel 2 documentation](https://cibuildwheel.pypa.io/en/2.x/) for
 </style>
 
 
-### `CIBW_ARCHS` {: #archs}
+### `archs` {: #archs cmd-line env-var toml }
 > Change the architectures built on your machine by default.
 
 A list of architectures to build.
@@ -188,10 +196,10 @@ Options:
     - `auto32`: Just the 32-bit auto archs
 - `native`: the native arch of the build machine - Matches [`platform.machine()`](https://docs.python.org/3/library/platform.html#platform.machine).
 - `all` : expands to all the architectures supported on this OS. You may want
-  to use [CIBW_BUILD](#build-skip) with this option to target specific
+  to use [`build`](#build-skip) with this option to target specific
   architectures via build selectors.
 
-Linux riscv64 platform support is experimental and requires an explicit opt-in through [CIBW_ENABLE](#enable).
+Linux riscv64 platform support is experimental and requires an explicit opt-in through [`enable`](#enable).
 
 Default: `auto`
 
@@ -203,7 +211,7 @@ Default: `auto`
 | macOS / Intel | `x86_64` | `x86_64` | `x86_64` |  |
 | macOS / Apple Silicon | `arm64` | `arm64` | `arm64` |  |
 | iOS on macOS / Intel | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` |  |
-| iOS on macOS / Apple Silicon | `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` |  |
+| iOS on macOS / Apple Silicon | `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` |
 
 If not listed above, `auto` is the same as `native`.
 
@@ -218,40 +226,42 @@ This option can also be set using the [command-line option](#command-line)
 
 #### Examples
 
-!!! tab examples "Environment variables"
+!!! tab examples "pyproject.toml"
 
-    ```yaml
+    ```toml
     # Build `universal2` and `arm64` wheels on an Intel runner.
     # Note that the `arm64` wheel and the `arm64` part of the `universal2`
     # wheel cannot be tested in this configuration.
-    CIBW_ARCHS_MACOS: "x86_64 universal2 arm64"
+    [tool.cibuildwheel.macos]
+    archs = ["x86_64", "universal2", "arm64"]
 
     # On an Linux Intel runner with qemu installed, build Intel and ARM wheels
-    CIBW_ARCHS_LINUX: "auto aarch64"
+    [tool.cibuildwheel.linux]
+    archs = ["auto", "aarch64"]
     ```
 
-    Separate multiple archs with a space.
-
-!!! tab examples "pyproject.toml"
+!!! tab examples "Environment variables"
 
-    ```toml
+    ```yaml
     # Build `universal2` and `arm64` wheels on an Intel runner.
     # Note that the `arm64` wheel and the `arm64` part of the `universal2`
     # wheel cannot be tested in this configuration.
-    [tool.cibuildwheel.macos]
-    archs = ["x86_64", "universal2", "arm64"]
+    CIBW_ARCHS_MACOS: "x86_64 universal2 arm64"
 
     # On an Linux Intel runner with qemu installed, build Intel and ARM wheels
-    [tool.cibuildwheel.linux]
-    archs = ["auto", "aarch64"]
+    CIBW_ARCHS_LINUX: "auto aarch64"
     ```
 
+    Separate multiple archs with a space.
+
+
+
     It is generally recommended to use the environment variable or
     command-line option for Linux, as selecting archs often depends
     on your specific runner having qemu installed.
 
 
-###  `CIBW_PROJECT_REQUIRES_PYTHON` {: #requires-python}
+###  `project-requires-python` {: #requires-python env-var}
 > Manually set the Python compatibility of your project
 
 By default, cibuildwheel reads your package's Python compatibility from
@@ -301,7 +311,7 @@ the package is compatible with all versions of Python that it can build.
     CIBW_PROJECT_REQUIRES_PYTHON: ">=3.8"
     ```
 
-###  `CIBW_ENABLE` {: #enable}
+### `enable` {: #enable toml env-var}
 > Enable building with extra categories of selectors present.
 
 This option lets you opt-in to non-default builds, like pre-releases and
@@ -343,16 +353,34 @@ This option doesn't support overrides or platform specific variants; it is
 intended as a way to acknowledge that a project is aware that these extra
 selectors exist.  If you need to enable/disable it per platform or python
 version, set this option to `true` and use
-[`CIBW_BUILD`](#build-skip)/[`CIBW_SKIP`](#build-skip) options to filter the
+[`build`](#build-skip)/[`skip`](#build-skip) options to filter the
 builds.
 
 Unlike all other cibuildwheel options, the environment variable setting will
 only add to the TOML config; you can't remove an enable by setting an empty or
-partial list in environment variables; use `CIBW_SKIP` instead.
+partial list in environment variables; use `CIBW_SKIP` instead. This way, if
+you apply `cpython-prerelease` during the beta period using `CIBW_ENABLE`
+without disabling your other enables.
 
 
 #### Examples
 
+!!! tab examples "pyproject.toml"
+
+    ```toml
+    [tool.cibuildwheel]
+    # Enable free-threaded support
+    enable = ["cpython-freethreading"]
+
+    # Skip building free-threaded compatible wheels on Windows
+    enable = ["cpython-freethreading"]
+    skip = "*t-win*"
+
+    # Include all PyPy versions
+    enable = ["pypy", "pypy-eol"]
+    ```
+
+
 !!! tab examples "Environment variables"
 
     ```yaml
@@ -368,28 +396,14 @@ partial list in environment variables; use `CIBW_SKIP` instead.
     # Skip building free-threaded compatible wheels on Windows
     CIBW_ENABLE: cpython-freethreading
     CIBW_SKIP: *t-win*
-    ```
-
-    It is generally recommended to use `cpython-freethreading` in a config
-    file as you can statically declare that you support free-threaded builds.
-
-!!! tab examples "pyproject.toml"
-
-    ```toml
-    [tool.cibuildwheel]
-    # Enable free-threaded support
-    enable = ["cpython-freethreading"]
 
-    # Skip building free-threaded compatible wheels on Windows
-    enable = ["cpython-freethreading"]
-    skip = "*t-win*"
+    # Include all PyPy versions
+    CIBW_ENABLE = pypy pypy-eol
     ```
 
-    It is generally not recommended to use `cpython-prerelease` in a config file,
-    as it's intended for testing pre-releases for a 2-3 month period only.
 
 
-### `CIBW_ALLOW_EMPTY` {: #allow-empty}
+### `allow-empty` {: #allow-empty cmd-line env-var}
 > Suppress the error code if no wheels match the specified build identifiers
 
 When none of the specified build identifiers match any available versions,
@@ -413,7 +427,7 @@ This option can also be set using the [command-line option](#command-line)
 
 ## Build customization
 
-### `CIBW_BUILD_FRONTEND` {: #build-frontend}
+### `build-frontend` {: #build-frontend toml env-var}
 > Set the tool to use to build, either "build" (default), "build\[uv\]", or "pip"
 
 Options:
@@ -452,46 +466,42 @@ optional `args` option.
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Switch to using build
-    CIBW_BUILD_FRONTEND: "build"
+!!! tab examples "pyproject.toml"
 
-    # Ensure pip is used even if the default changes in the future
-    CIBW_BUILD_FRONTEND: "pip"
+    ```toml
+    [tool.cibuildwheel]
+    # Switch to using pip
+    build-frontend = "pip"
 
     # supply an extra argument to 'pip wheel'
-    CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
+    build-frontend = { name = "pip", args = ["--no-build-isolation"] }
 
     # Use uv and build
-    CIBW_BUILD_FRONTEND: "build[uv]"
+    build-frontend = "build[uv]"
 
     # Use uv and build with an argument
-    CIBW_BUILD_FRONTEND: "build[uv]; args: --no-isolation"
+    build-frontend = { name = "build[uv]", args = ["--no-isolation"] }
     ```
 
-!!! tab examples "pyproject.toml"
-
-    ```toml
-    [tool.cibuildwheel]
-    # Switch to using build
-    build-frontend = "build"
+!!! tab examples "Environment variables"
 
-    # Ensure pip is used even if the default changes in the future
-    build-frontend = "pip"
+    ```yaml
+    # Switch to using pip
+    CIBW_BUILD_FRONTEND: "pip"
 
     # supply an extra argument to 'pip wheel'
-    build-frontend = { name = "pip", args = ["--no-build-isolation"] }
+    CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
 
     # Use uv and build
-    build-frontend = "build[uv]"
+    CIBW_BUILD_FRONTEND: "build[uv]"
 
     # Use uv and build with an argument
-    build-frontend = { name = "build[uv]", args = ["--no-isolation"] }
+    CIBW_BUILD_FRONTEND: "build[uv]; args: --no-isolation"
     ```
 
-### `CIBW_CONFIG_SETTINGS` {: #config-settings}
+
+
+### `config-settings` {: #config-settings env-var toml}
 > Specify config-settings for the build backend.
 
 Specify config settings for the build backend. Each space separated
@@ -508,21 +518,23 @@ Platform-specific environment variables also available:<br/>
 
 #### Examples
 
+!!! tab examples "pyproject.toml"
+
+    ```toml
+    [tool.cibuildwheel.config-settings]
+    --build-option = "--use-mypyc"
+    ```
+
 !!! tab examples "Environment variables"
 
     ```yaml
     CIBW_CONFIG_SETTINGS: "--build-option=--use-mypyc"
     ```
 
-!!! tab examples "pyproject.toml"
 
-    ```toml
-    [tool.cibuildwheel.config-settings]
-    --build-option = "--use-mypyc"
-    ```
 
 
-### `CIBW_ENVIRONMENT` {: #environment}
+### `environment` {: #environment env-var toml}
 > Set environment variables
 
 A list of environment variables to set during the build and test phases. Bash syntax should be used, even on Windows.
@@ -538,35 +550,6 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Set some compiler flags
-    CIBW_ENVIRONMENT: CFLAGS='-g -Wall' CXXFLAGS='-Wall'
-
-    # Append a directory to the PATH variable (this is expanded in the build environment)
-    CIBW_ENVIRONMENT: PATH=$PATH:/usr/local/bin
-
-    # Prepend a directory containing spaces on Windows.
-    CIBW_ENVIRONMENT_WINDOWS: >
-      PATH="C:\\Program Files\\PostgreSQL\\13\\bin;$PATH"
-
-    # Set BUILD_TIME to the output of the `date` command
-    CIBW_ENVIRONMENT: BUILD_TIME="$(date)"
-
-    # Supply options to `pip` to affect how it downloads dependencies
-    CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.myorg.com/simple
-
-    # Any pip command-line options can be set using the PIP_ prefix
-    # https://pip.pypa.io/en/stable/topics/configuration/#environment-variables
-    CIBW_ENVIRONMENT: PIP_GLOBAL_OPTION="build_ext -j4"
-
-    # Set two flags on linux only
-    CIBW_ENVIRONMENT_LINUX: BUILD_TIME="$(date)" SAMPLE_TEXT="sample text"
-    ```
-
-    Separate multiple values with a space.
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -604,7 +587,36 @@ Platform-specific environment variables are also available:<br/>
     SAMPLE_TEXT = "sample text"
     ```
 
-    In configuration mode, you can use a [TOML][] table instead of a raw string as shown above.
+    In configuration files, you can use a [TOML][] table instead of a raw string as shown above.
+
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Set some compiler flags
+    CIBW_ENVIRONMENT: CFLAGS='-g -Wall' CXXFLAGS='-Wall'
+
+    # Append a directory to the PATH variable (this is expanded in the build environment)
+    CIBW_ENVIRONMENT: PATH=$PATH:/usr/local/bin
+
+    # Prepend a directory containing spaces on Windows.
+    CIBW_ENVIRONMENT_WINDOWS: >
+      PATH="C:\\Program Files\\PostgreSQL\\13\\bin;$PATH"
+
+    # Set BUILD_TIME to the output of the `date` command
+    CIBW_ENVIRONMENT: BUILD_TIME="$(date)"
+
+    # Supply options to `pip` to affect how it downloads dependencies
+    CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.myorg.com/simple
+
+    # Any pip command-line options can be set using the PIP_ prefix
+    # https://pip.pypa.io/en/stable/topics/configuration/#environment-variables
+    CIBW_ENVIRONMENT: PIP_GLOBAL_OPTION="build_ext -j4"
+
+    # Set two flags on linux only
+    CIBW_ENVIRONMENT_LINUX: BUILD_TIME="$(date)" SAMPLE_TEXT="sample text"
+    ```
+
+    Separate multiple values with a space.
 
 !!! note
     cibuildwheel always defines the environment variable `CIBUILDWHEEL=1`. This can be useful for [building wheels with optional extensions](faq.md#optional-extensions).
@@ -612,7 +624,7 @@ Platform-specific environment variables are also available:<br/>
 !!! note
     To do its work, cibuildwheel sets the variables `VIRTUALENV_PIP`, `DIST_EXTRA_CONFIG`, `SETUPTOOLS_EXT_SUFFIX`, `PIP_DISABLE_PIP_VERSION_CHECK`, `PIP_ROOT_USER_ACTION`, and it extends the variables `PATH` and `PIP_CONSTRAINT`. Your assignments to these options might be replaced or extended.
 
-### `CIBW_ENVIRONMENT_PASS_LINUX` {: #environment-pass}
+### `environment-pass` {: #environment-pass env-var="CIBW_ENVIRONMENT_PASS_LINUX" toml}
 > Set environment variables on the host to pass-through to the container.
 
 A list of environment variables to pass into the linux container during each build and test. It has no effect on the other platforms, which can already access all environment variables directly.
@@ -624,33 +636,33 @@ To specify more than one environment variable, separate the variable names by sp
 
 #### Examples
 
-!!! tab examples "Environment passthrough"
+!!! tab examples "pyproject.toml"
+
+    ```toml
+    [tool.cibuildwheel.linux]
 
-    ```yaml
     # Export a variable
-    CIBW_ENVIRONMENT_PASS_LINUX: CFLAGS
+    environment-pass = ["CFLAGS"]
 
     # Set two flags variables
-    CIBW_ENVIRONMENT_PASS_LINUX: BUILD_TIME SAMPLE_TEXT
+    environment-pass = ["BUILD_TIME", "SAMPLE_TEXT"]
     ```
 
-    Separate multiple values with a space.
-
-!!! tab examples "pyproject.toml"
+    In configuration files, you can use a [TOML][] list instead of a raw string as shown above.
 
-    ```toml
-    [tool.cibuildwheel.linux]
+!!! tab examples "Environment variables"
 
+    ```yaml
     # Export a variable
-    environment-pass = ["CFLAGS"]
+    CIBW_ENVIRONMENT_PASS_LINUX: CFLAGS
 
     # Set two flags variables
-    environment-pass = ["BUILD_TIME", "SAMPLE_TEXT"]
+    CIBW_ENVIRONMENT_PASS_LINUX: BUILD_TIME SAMPLE_TEXT
     ```
 
-    In configuration mode, you can use a [TOML][] list instead of a raw string as shown above.
+    Separate multiple values with a space.
 
-### `CIBW_BEFORE_ALL` {: #before-all}
+### `before-all` {: #before-all env-var toml}
 > Execute a shell command on the build system before any wheels are built.
 
 Shell command that runs before any builds are run, to build or install parts that do not depend on the specific version of Python.
@@ -659,7 +671,7 @@ This option is very useful for the Linux build, where builds take place in isola
 
 The placeholder `{package}` can be used here; it will be replaced by the path to the package being built by cibuildwheel.
 
-On Windows and macOS, the version of Python available inside `CIBW_BEFORE_ALL` is whatever is available on the host machine. On Linux, a modern Python version is available on PATH.
+On Windows and macOS, the version of Python available inside `before-all` is whatever is available on the host machine. On Linux, a modern Python version is available on PATH.
 
 This option has special behavior in the overrides section in `pyproject.toml`.
 On linux, overriding it triggers a new container launch. It cannot be overridden
@@ -670,30 +682,10 @@ Platform-specific environment variables also available:<br/>
 
 !!! note
 
-    This command is executed in a different Python environment from the builds themselves. So you can't `pip install` a Python dependency in CIBW_BEFORE_ALL and use it in the build. Instead, look at [`CIBW_BEFORE_BUILD`](#before-build), or, if your project uses pyproject.toml, the [build-system.requires](https://peps.python.org/pep-0518/#build-system-table) field.
+    This command is executed in a different Python environment from the builds themselves. So you can't `pip install` a Python dependency in `before-all` and use it in the build. Instead, look at [`before-build`](#before-build), or, if your project uses pyproject.toml, the [build-system.requires](https://peps.python.org/pep-0518/#build-system-table) field.
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Build third party library
-    CIBW_BEFORE_ALL: make -C third_party_lib
-
-    # Install system library
-    CIBW_BEFORE_ALL_LINUX: yum install -y libffi-devel
-
-    # Chain multiple commands using && and > in a YAML file, like:
-    CIBW_BEFORE_ALL: >
-      yum install bzip2 -y &&
-      make third_party
-    ```
-
-    For multiline commands, see the last example. The character `>` means that
-    whitespace is collapsed to a single line, and '&&' between each command
-    ensures that errors are not ignored. [Further reading on multiline YAML
-    here.](https://yaml-multiline.info).
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -714,14 +706,36 @@ Platform-specific environment variables also available:<br/>
 
     In configuration files, you can use a TOML array, and each line will be run sequentially - joined with `&&`.
 
-Note that manylinux_2_31 builds occur inside a debian derivative docker container, where
-manylinux2014 builds occur inside a CentOS one. So for `manylinux_2_31` the `CIBW_BEFORE_ALL_LINUX` command
-must use `apt-get -y` instead.
-
-### `CIBW_BEFORE_BUILD` {: #before-build}
-> Execute a shell command preparing each wheel's build
+!!! tab examples "Environment variables"
 
-A shell command to run before building the wheel. This option allows you to run a command in **each** Python environment before the `pip wheel` command. This is useful if you need to set up some dependency so it's available during the build.
+    ```yaml
+    # Build third party library
+    CIBW_BEFORE_ALL: make -C third_party_lib
+
+    # Install system library
+    CIBW_BEFORE_ALL_LINUX: yum install -y libffi-devel
+
+    # Chain multiple commands using && and > in a YAML file, like:
+    CIBW_BEFORE_ALL: >
+      yum install bzip2 -y &&
+      make third_party
+    ```
+
+    For multiline commands, see the last example. The character `>` means that
+    whitespace is collapsed to a single line, and '&&' between each command
+    ensures that errors are not ignored. [Further reading on multiline YAML
+    here.](https://yaml-multiline.info).
+
+
+Note that `manylinux_2_31` builds occur inside a Debian derivative docker
+container, where `manylinux2014` builds occur inside a CentOS one. So for
+`manylinux_2_31` the `before-all` command must use `apt-get -y`
+instead.
+
+### `before-build` {: #before-build env-var toml}
+> Execute a shell command preparing each wheel's build
+
+A shell command to run before building the wheel. This option allows you to run a command in **each** Python environment before the `pip wheel` command. This is useful if you need to set up some dependency so it's available during the build.
 
 If dependencies are required to build your wheel (for example if you include a header from a Python module), instead of using this command, we recommend adding requirements to a `pyproject.toml` file's `build-system.requires` array instead. This is reproducible, and users who do not get your wheels (such as Alpine or ClearLinux users) will still benefit.
 
@@ -734,22 +748,6 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Install something required for the build (you might want to use pyproject.toml instead)
-    CIBW_BEFORE_BUILD: pip install pybind11
-
-    # Chain commands using &&
-    CIBW_BEFORE_BUILD_LINUX: python scripts/install-deps.py && make clean
-
-    # Run a script that's inside your project
-    CIBW_BEFORE_BUILD: bash scripts/prepare_for_build.sh
-
-    # If cibuildwheel is called with a package_dir argument, it's available as {package}
-    CIBW_BEFORE_BUILD: "{package}/script/prepare_for_build.sh"
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -773,9 +771,27 @@ Platform-specific environment variables are also available:<br/>
     before-build = "{package}/script/prepare_for_build.sh"
     ```
 
-    In configuration mode, you can use a array, and the items will be joined with `&&`. In TOML, using a single-quote string will avoid escapes - useful for
+    In configuration files, you can use a array, and the items will be joined
+    with `&&`. In TOML, using a single-quote string will avoid escapes - useful for
     Windows paths.
 
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Install something required for the build (you might want to use pyproject.toml instead)
+    CIBW_BEFORE_BUILD: pip install pybind11
+
+    # Chain commands using &&
+    CIBW_BEFORE_BUILD_LINUX: python scripts/install-deps.py && make clean
+
+    # Run a script that's inside your project
+    CIBW_BEFORE_BUILD: bash scripts/prepare_for_build.sh
+
+    # If cibuildwheel is called with a package_dir argument, it's available as {package}
+    CIBW_BEFORE_BUILD: "{package}/script/prepare_for_build.sh"
+    ```
+
+
 !!! note
     If you need Python dependencies installed for the build, we recommend using
     `pyproject.toml`'s `build-system.requires` instead. This is an example
@@ -785,8 +801,7 @@ Platform-specific environment variables are also available:<br/>
         requires = [
             "setuptools>=42",
             "Cython",
-            "numpy==1.13.3; python_version<'3.5'",
-            "oldest-supported-numpy; python_version>='3.5'",
+            "numpy",
         ]
 
         build-backend = "setuptools.build_meta"
@@ -794,59 +809,53 @@ Platform-specific environment variables are also available:<br/>
     This [PEP 517][]/[PEP 518][] style build allows you to completely control
     the build environment in cibuildwheel, [PyPA-build][], and pip, doesn't
     force downstream users to install anything they don't need, and lets you do
-    more complex pinning (Cython, for example, requires a wheel to be built
-    with an equal or earlier version of NumPy; pinning in this way is the only
-    way to ensure your module works on all available NumPy versions).
+    more complex pinning.
 
     [PyPA-build]: https://pypa-build.readthedocs.io/en/latest/
     [PEP 517]: https://www.python.org/dev/peps/pep-0517/
     [PEP 518]: https://www.python.org/dev/peps/pep-0517/
 
-### `CIBW_XBUILD_TOOLS` {: #xbuild-tools}
+### `xbuild-tools` {: #xbuild-tools env-var toml}
 > Binaries on the path that should be included in an isolated cross-build environment.
 
 When building in a cross-platform environment, it is sometimes necessary to isolate the ``PATH`` so that binaries from the build machine don't accidentally get linked into the cross-platform binary. However, this isolation process will also hide tools that might be required to build your wheel.
 
-If there are binaries present on the `PATH` when you invoke cibuildwheel, and those binaries are required to build your wheels, those binaries can be explicitly included in the isolated cross-build environment using `CIBW_XBUILD_TOOLS`. The binaries listed in this setting will be linked into an isolated location, and that isolated location will be put on the `PATH` of the isolated environment. You do not need to provide the full path to the binary - only the executable name that would be found by the shell.
+If there are binaries present on the `PATH` when you invoke cibuildwheel, and those binaries are required to build your wheels, those binaries can be explicitly included in the isolated cross-build environment using `xbuild-tools`. The binaries listed in this setting will be linked into an isolated location, and that isolated location will be put on the `PATH` of the isolated environment. You do not need to provide the full path to the binary - only the executable name that would be found by the shell.
 
 If you declare a tool as a cross-build tool, and that tool cannot be found in the runtime environment, an error will be raised.
 
-If you do not define `CIBW_XBUILD_TOOLS`, and you build for a platform that uses a cross-platform environment, a warning will be raised. If your project does not require any cross-build tools, you can set `CIBW_XBUILD_TOOLS` to an empty list to silence this warning.
+If you do not define `xbuild-tools`, and you build for a platform that uses a cross-platform environment, a warning will be raised. If your project does not require any cross-build tools, you can set `xbuild-tools` to an empty list to silence this warning.
 
-*Any* tool used by the build process must be included in the `CIBW_XBUILD_TOOLS` list, not just tools that cibuildwheel will invoke directly. For example, if your build invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your safe tools list.
+*Any* tool used by the build process must be included in the `xbuild-tools` list, not just tools that cibuildwheel will invoke directly. For example, if your build invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your safe tools list.
 
 Platform-specific environment variables are also available on platforms that use cross-platform environment isolation:<br/>
  `CIBW_XBUILD_TOOLS_IOS`
 
 #### Examples
 
-!!! tab examples "Environment variables"
+!!! tab examples "pyproject.toml"
 
-    ```yaml
+    ```toml
+    [tool.cibuildwheel]
     # Allow access to the cmake and rustc binaries in the isolated cross-build environment.
-    CIBW_XBUILD_TOOLS: cmake rustc
-    ```
+    xbuild-tools = ["cmake", "rustc"]
 
-    ```yaml
     # No cross-build tools are required
-    CIBW_XBUILD_TOOLS:
+    xbuild-tools = []
     ```
 
-!!! tab examples "pyproject.toml"
+!!! tab examples "Environment variables"
 
-    ```toml
-    [tool.cibuildwheel]
+    ```yaml
     # Allow access to the cmake and rustc binaries in the isolated cross-build environment.
-    xbuild-tools = ["cmake", "rustc"]
-    ```
+    CIBW_XBUILD_TOOLS: cmake rustc
 
-    ```toml
-    [tool.cibuildwheel]
     # No cross-build tools are required
-    xbuild-tools = []
+    CIBW_XBUILD_TOOLS:
     ```
 
-### `CIBW_REPAIR_WHEEL_COMMAND` {: #repair-wheel-command}
+
+### `repair-wheel-command` {: #repair-wheel-command env-var toml}
 > Execute a shell command to repair each built wheel
 
 Default:
@@ -888,36 +897,6 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Use delvewheel on windows
-    CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel"
-    CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}"
-
-    # Don't repair macOS wheels
-    CIBW_REPAIR_WHEEL_COMMAND_MACOS: ""
-
-    # Pass the `--lib-sdir .` flag to auditwheel on Linux
-    CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
-
-    # Multi-line example - use && to join on all platforms
-    CIBW_REPAIR_WHEEL_COMMAND: >
-      python scripts/repair_wheel.py -w {dest_dir} {wheel} &&
-      python scripts/check_repaired_wheel.py -w {dest_dir} {wheel}
-
-    # Use abi3audit to catch issues with Limited API wheels
-    CIBW_REPAIR_WHEEL_COMMAND_LINUX: >
-      auditwheel repair -w {dest_dir} {wheel} &&
-      pipx run abi3audit --strict --report {wheel}
-    CIBW_REPAIR_WHEEL_COMMAND_MACOS: >
-      delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} &&
-      pipx run abi3audit --strict --report {wheel}
-    CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >
-      copy {wheel} {dest_dir} &&
-      pipx run abi3audit --strict --report {wheel}
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -959,45 +938,76 @@ Platform-specific environment variables are also available:<br/>
     ]
     ```
 
-    In configuration mode, you can use an inline array, and the items will be joined with `&&`.
+    In configuration files, you can use an inline array, and the items will be joined with `&&`.
+
+
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Use delvewheel on windows
+    CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel"
+    CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}"
+
+    # Don't repair macOS wheels
+    CIBW_REPAIR_WHEEL_COMMAND_MACOS: ""
+
+    # Pass the `--lib-sdir .` flag to auditwheel on Linux
+    CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
+
+    # Multi-line example - use && to join on all platforms
+    CIBW_REPAIR_WHEEL_COMMAND: >
+      python scripts/repair_wheel.py -w {dest_dir} {wheel} &&
+      python scripts/check_repaired_wheel.py -w {dest_dir} {wheel}
+
+    # Use abi3audit to catch issues with Limited API wheels
+    CIBW_REPAIR_WHEEL_COMMAND_LINUX: >
+      auditwheel repair -w {dest_dir} {wheel} &&
+      pipx run abi3audit --strict --report {wheel}
+    CIBW_REPAIR_WHEEL_COMMAND_MACOS: >
+      delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} &&
+      pipx run abi3audit --strict --report {wheel}
+    CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >
+      copy {wheel} {dest_dir} &&
+      pipx run abi3audit --strict --report {wheel}
+    ```
 
 
 <div class="link-target" id="manylinux-image"></div>
 
-### `CIBW_MANYLINUX_*_IMAGE`, `CIBW_MUSLLINUX_*_IMAGE` {: #linux-image}
+### `manylinux-*-image`, `musllinux-*-image` {: #linux-image env-var toml}
 
 > Specify manylinux / musllinux container images
 
 The available options are:
 
-| Option                            | Default                                                         |
-|-----------------------------------|-----------------------------------------------------------------|
-| CIBW_MANYLINUX_X86_64_IMAGE       | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
-| CIBW_MANYLINUX_I686_IMAGE         | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
-| CIBW_MANYLINUX_PYPY_X86_64_IMAGE  | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
-| CIBW_MANYLINUX_AARCH64_IMAGE      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
-| CIBW_MANYLINUX_PPC64LE_IMAGE      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
-| CIBW_MANYLINUX_S390X_IMAGE        | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x)   |
-| CIBW_MANYLINUX_ARMV7L_IMAGE       | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l)  |
-| CIBW_MANYLINUX_RISCV64_IMAGE      | No default                                                      |
-| CIBW_MANYLINUX_PYPY_AARCH64_IMAGE | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
-| CIBW_MANYLINUX_PYPY_I686_IMAGE    | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
-| CIBW_MUSLLINUX_X86_64_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64)    |
-| CIBW_MUSLLINUX_I686_IMAGE         | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686)      |
-| CIBW_MUSLLINUX_AARCH64_IMAGE      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64)   |
-| CIBW_MUSLLINUX_PPC64LE_IMAGE      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le)   |
-| CIBW_MUSLLINUX_S390X_IMAGE        | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x)     |
-| CIBW_MUSLLINUX_ARMV7L_IMAGE       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l)    |
-| CIBW_MUSLLINUX_RISCV64_IMAGE      | No default                                                      |
+| Option                         | Default                                                         |
+|--------------------------------|-----------------------------------------------------------------|
+| `manylinux-x86-64-image`       | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-x86-64)  |
+| `manylinux-i686-image`         | [`manylinux2014`](https://quay.io/pypa/manylinux2014-i686)      |
+| `manylinux-pypy-x86-64-image`  | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-x86-64)  |
+| `manylinux-aarch64-image`      | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-aarch64) |
+| `manylinux-ppc64le-image`      | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-ppc64le) |
+| `manylinux-s390x-image`        | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-s390x)   |
+| `manylinux-armv7l-image`       | [`manylinux-2-31`](https://quay.io/pypa/manylinux-2-31-armv7l)  |
+| `manylinux-riscv64-image`      | No default                                                      |
+| `manylinux-pypy-aarch64-image` | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-aarch64) |
+| `manylinux-pypy-i686-image`    | [`manylinux2014`](https://quay.io/pypa/manylinux2014-i686)      |
+| `musllinux-x86-64-image`       | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-x86-64)    |
+| `musllinux-i686-image`         | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-i686)      |
+| `musllinux-aarch64-image`      | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-aarch64)   |
+| `musllinux-ppc64le-image`      | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-ppc64le)   |
+| `musllinux-s390x-image`        | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-s390x)     |
+| `musllinux-armv7l-image`       | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-armv7l)    |
+| `musllinux-riscv64-image`      | No default                                                      |
 
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
 
-For `CIBW_MANYLINUX_*_IMAGE`, except `CIBW_MANYLINUX_ARMV7L_IMAGE`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
+For `manylinux-*-image`, except `manylinux-armv7l-image`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
 `manylinux_2_28` and `manylinux_2_34` are not supported for `i686` architecture.
 
-For `CIBW_MANYLINUX_ARMV7L_IMAGE`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
+For `manylinux-armv7l-image`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
 
-For `CIBW_MUSLLINUX_*_IMAGE`, the value of this option can either be set to `musllinux_1_2` or a custom image.
+For `musllinux-*-image`, the value of this option can either be set to `musllinux_1_2` or a custom image.
 
 If this option is blank, it will fall though to the next available definition (environment variable -> pyproject.toml -> default).
 
@@ -1023,30 +1033,6 @@ Auditwheel detects the version of the manylinux / musllinux standard in the imag
 #### Examples
 
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Build using the manylinux2014 image
-    CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
-    CIBW_MANYLINUX_I686_IMAGE: manylinux2014
-    CIBW_MANYLINUX_PYPY_X86_64_IMAGE: manylinux2014
-    CIBW_MANYLINUX_PYPY_I686_IMAGE: manylinux2014
-
-    # Build using the latest manylinux2010 release
-    CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
-    CIBW_MANYLINUX_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
-    CIBW_MANYLINUX_PYPY_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
-    CIBW_MANYLINUX_PYPY_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
-
-    # Build using a different image from the docker registry
-    CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
-    CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
-
-    # Build musllinux wheels using the musllinux_1_1 image
-    CIBW_MUSLLINUX_X86_64_IMAGE: quay.io/pypa/musllinux_1_1_x86_64:latest
-    CIBW_MUSLLINUX_I686_IMAGE: quay.io/pypa/musllinux_1_1_i686:latest
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -1075,8 +1061,32 @@ Auditwheel detects the version of the manylinux / musllinux standard in the imag
     Like any other option, these can be placed in `[tool.cibuildwheel.linux]`
     if you prefer; they have no effect on `macos` and `windows`.
 
+!!! tab examples "Environment variables"
 
-### `CIBW_CONTAINER_ENGINE` {: #container-engine}
+    ```yaml
+    # Build using the manylinux2014 image
+    CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
+    CIBW_MANYLINUX_I686_IMAGE: manylinux2014
+    CIBW_MANYLINUX_PYPY_X86_64_IMAGE: manylinux2014
+    CIBW_MANYLINUX_PYPY_I686_IMAGE: manylinux2014
+
+    # Build using the latest manylinux2010 release
+    CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
+    CIBW_MANYLINUX_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
+    CIBW_MANYLINUX_PYPY_X86_64_IMAGE: quay.io/pypa/manylinux2010_x86_64:latest
+    CIBW_MANYLINUX_PYPY_I686_IMAGE: quay.io/pypa/manylinux2010_i686:latest
+
+    # Build using a different image from the docker registry
+    CIBW_MANYLINUX_X86_64_IMAGE: dockcross/manylinux-x64
+    CIBW_MANYLINUX_I686_IMAGE: dockcross/manylinux-x86
+
+    # Build musllinux wheels using the musllinux_1_1 image
+    CIBW_MUSLLINUX_X86_64_IMAGE: quay.io/pypa/musllinux_1_1_x86_64:latest
+    CIBW_MUSLLINUX_I686_IMAGE: quay.io/pypa/musllinux_1_1_i686:latest
+    ```
+
+
+### `container-engine` {: #container-engine env-var toml}
 > Specify the container engine to use when building Linux wheels
 
 Options:
@@ -1110,35 +1120,36 @@ Options can be supplied after the name.
 
 #### Examples
 
-!!! tab examples "Environment variables"
+!!! tab examples "pyproject.toml"
 
-    ```yaml
+    ```toml
+    [tool.cibuildwheel]
     # use podman instead of docker
-    CIBW_CONTAINER_ENGINE: podman
+    container-engine = "podman"
 
     # pass command line options to 'docker create'
-    CIBW_CONTAINER_ENGINE: "docker; create_args: --gpus all"
+    container-engine = { name = "docker", create-args = ["--gpus", "all"]}
 
     # disable the /host mount
-    CIBW_CONTAINER_ENGINE: "docker; disable_host_mount: true"
+    container-engine = { name = "docker", disable-host-mount = true }
     ```
 
-!!! tab examples "pyproject.toml"
+!!! tab examples "Environment variables"
 
-    ```toml
-    [tool.cibuildwheel]
+    ```yaml
     # use podman instead of docker
-    container-engine = "podman"
+    CIBW_CONTAINER_ENGINE: podman
 
     # pass command line options to 'docker create'
-    container-engine = { name = "docker", create-args = ["--gpus", "all"]}
+    CIBW_CONTAINER_ENGINE: "docker; create_args: --gpus all"
 
     # disable the /host mount
-    container-engine = { name = "docker", disable-host-mount = true }
+    CIBW_CONTAINER_ENGINE: "docker; disable_host_mount: true"
     ```
 
 
-### `CIBW_DEPENDENCY_VERSIONS` {: #dependency-versions}
+
+### `dependency-versions` {: #dependency-versions env-var toml}
 
 > Control the versions of the tools cibuildwheel uses
 
@@ -1146,7 +1157,7 @@ Options: `pinned` `latest` `packages: SPECIFIER...` `<your constraints file>`
 
 Default: `pinned`
 
-If `CIBW_DEPENDENCY_VERSIONS` is `pinned`, cibuildwheel uses versions of tools
+If `dependency-versions` is `pinned`, cibuildwheel uses versions of tools
 like `pip`, `setuptools`, `virtualenv` that were pinned with that release of
 cibuildwheel. This represents a known-good set of dependencies, and is
 recommended for build repeatability.
@@ -1163,7 +1174,7 @@ specifiers inline with the `packages: SPECIFIER...` syntax.
 !!! note
     If you need different dependencies for each python version, provide them
     in the same folder with a `-pythonXY` suffix. e.g. if your
-    `CIBW_DEPENDENCY_VERSIONS=./constraints.txt`, cibuildwheel will use
+    `dependency-versions="./constraints.txt"`, cibuildwheel will use
     `./constraints-python38.txt` on Python 3.8, or fallback to
     `./constraints.txt` if that's not found.
 
@@ -1173,55 +1184,57 @@ Platform-specific environment variables are also available:<br/>
 !!! note
     This option does not affect the tools used on the Linux build - those versions
     are bundled with the manylinux/musllinux image that cibuildwheel uses. To change
-    dependency versions on Linux, use the [CIBW_MANYLINUX_* / CIBW_MUSLLINUX_*](#linux-image)
+    dependency versions on Linux, use the [`manylinux-*` / `musllinux-*`](#linux-image)
     options.
 
 #### Examples
 
-!!! tab examples "Environment variables"
+!!! tab examples "pyproject.toml"
 
-    ```yaml
+    ```toml
+    [tool.cibuildwheel]
     # Use tools versions that are bundled with cibuildwheel (this is the default)
-    CIBW_DEPENDENCY_VERSIONS: pinned
+    dependency-versions = "pinned"
 
     # Use the latest versions available on PyPI
-    CIBW_DEPENDENCY_VERSIONS: latest
+    dependency-versions = "latest"
 
     # Use your own pip constraints file
-    CIBW_DEPENDENCY_VERSIONS: ./constraints.txt
+    dependency-versions = { file = "./constraints.txt" }
 
     # Specify requirements inline
-    CIBW_DEPENDENCY_VERSIONS: "packages: auditwheel==6.2.0"
+    dependency-versions = { packages = ["auditwheel==6.2.0"] }
 
+    [tool.cibuildwheel.pyodide]
     # Choose a specific pyodide-build version
-    CIBW_DEPENDENCY_VERSIONS_PYODIDE: "packages: pyodide-build==0.29.1"
-
-    # Use shell-style quoting around spaces package specifiers
-    CIBW_DEPENDENCY_VERSIONS: "packages: 'pip >=16.0.0, !=17'"
+    dependency-versions = { packages = ["pyodide-build==0.29.1"] }
     ```
 
-!!! tab examples "pyproject.toml"
+!!! tab examples "Environment variables"
 
-    ```toml
-    [tool.cibuildwheel]
+    ```yaml
     # Use tools versions that are bundled with cibuildwheel (this is the default)
-    dependency-versions = "pinned"
+    CIBW_DEPENDENCY_VERSIONS: pinned
 
     # Use the latest versions available on PyPI
-    dependency-versions = "latest"
+    CIBW_DEPENDENCY_VERSIONS: latest
 
     # Use your own pip constraints file
-    dependency-versions = { file = "./constraints.txt" }
+    CIBW_DEPENDENCY_VERSIONS: ./constraints.txt
 
     # Specify requirements inline
-    dependency-versions = { packages = ["auditwheel==6.2.0"] }
+    CIBW_DEPENDENCY_VERSIONS: "packages: auditwheel==6.2.0"
 
-    [tool.cibuildwheel.pyodide]
     # Choose a specific pyodide-build version
-    dependency-versions = { packages = ["pyodide-build==0.29.1"] }
+    CIBW_DEPENDENCY_VERSIONS_PYODIDE: "packages: pyodide-build==0.29.1"
+
+    # Use shell-style quoting around spaces package specifiers
+    CIBW_DEPENDENCY_VERSIONS: "packages: 'pip >=16.0.0, !=17'"
     ```
 
-### `CIBW_PYODIDE_VERSION` {: #pyodide-version}
+
+
+### `pyodide-version` {: #pyodide-version toml env-var }
 
 > Specify the Pyodide version to use for `pyodide` platform builds
 
@@ -1235,20 +1248,10 @@ This option is particularly useful for:
 The available Pyodide versions are determined by the version of `pyodide-build` being used. You can list the compatible versions using the command `pyodide xbuildenv search --all` as described in the [Pyodide platform documentation](platforms.md#pyodide-choosing-a-version).
 
 !!! tip
-    You can set the version of `pyodide-build` using the [`CIBW_DEPENDENCY_VERSIONS`](#dependency-versions) option.
+    You can set the version of `pyodide-build` using the [`dependency-versions`](#dependency-versions) option.
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Build Pyodide wheels using Pyodide version 0.27.6
-    CIBW_PYODIDE_VERSION: 0.27.6
-
-    # Build Pyodide wheels using a specific alpha release
-    CIBW_PYODIDE_VERSION: 0.28.0a2
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -1261,9 +1264,20 @@ The available Pyodide versions are determined by the version of `pyodide-build`
     pyodide-version = "0.28.0a2"
     ```
 
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Build Pyodide wheels using Pyodide version 0.27.6
+    CIBW_PYODIDE_VERSION: 0.27.6
+
+    # Build Pyodide wheels using a specific alpha release
+    CIBW_PYODIDE_VERSION: 0.28.0a2
+    ```
+
+
 ## Testing
 
-### `CIBW_TEST_COMMAND` {: #test-command}
+### `test-command` {: #test-command env-var toml}
 > The command to test each built wheel
 
 Shell command to run tests after the build. The wheel will be installed
@@ -1271,7 +1285,7 @@ automatically and available for import from the tests. If this variable is not
 set, your wheel will not be installed after building.
 
 By default, tests are executed from your project directory. When specifying
-`CIBW_TEST_COMMAND`, you can optionally use the placeholders `{package}` and
+`test-command`, you can optionally use the placeholders `{package}` and
 `{project}` to pass in the location of your test code:
 
 - `{package}` is the path to the package being built - the `package_dir`
@@ -1284,38 +1298,21 @@ Using `{package}` or `{project}` used to be required, but since cibuildwheel
 use relative paths in your test command, and they will be relative to the
 project root.
 
-Alternatively, you can use the [`CIBW_TEST_SOURCES`](#test-sources) setting to
+Alternatively, you can use the [`test-sources`](#test-sources) setting to
 create a temporary folder populated with a specific subset of project files to
 run your test suite.
 
 On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
 
-On iOS, the value of the `CIBW_TEST_COMMAND` setting must follow the format `python -m MODULE [ARGS...]` - where MODULE is a Python module name, followed by arguments that will be assigned to `sys.argv`. Other commands cannot be used.
+On iOS, the value of the `test-command` setting must follow the format `python
+-m MODULE [ARGS...]` - where MODULE is a Python module name, followed by
+arguments that will be assigned to `sys.argv`. Other commands cannot be used.
 
 Platform-specific environment variables are also available:<br/>
 `CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE`
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Run the package tests using `pytest`
-    CIBW_TEST_COMMAND: pytest ./tests
-
-    # Trigger an install of the package, but run nothing of note
-    CIBW_TEST_COMMAND: "echo Wheel installed"
-
-    # Multi-line example - join with && on all platforms
-    CIBW_TEST_COMMAND: >
-      pytest ./tests &&
-      python ./test.py
-
-    # run tests on ios
-    CIBW_TEST_SOURCES_IOS: tests
-    CIBW_TEST_COMMAND_IOS: python -m pytest ./tests
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -1340,7 +1337,26 @@ Platform-specific environment variables are also available:<br/>
 
     In configuration files, you can use an array, and the items will be joined with `&&`.
 
-### `CIBW_BEFORE_TEST` {: #before-test}
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Run the package tests using `pytest`
+    CIBW_TEST_COMMAND: pytest ./tests
+
+    # Trigger an install of the package, but run nothing of note
+    CIBW_TEST_COMMAND: "echo Wheel installed"
+
+    # Multi-line example - join with && on all platforms
+    CIBW_TEST_COMMAND: >
+      pytest ./tests &&
+      python ./test.py
+
+    # run tests on ios
+    CIBW_TEST_SOURCES_IOS: tests
+    CIBW_TEST_COMMAND_IOS: python -m pytest ./tests
+    ```
+
+### `before-test` {: #before-test env-var toml}
 > Execute a shell command before testing each wheel
 
 A shell command to run in **each** test virtual environment, before your wheel is installed and tested. This is useful if you need to install a non-pip package, invoke pip with different environment variables,
@@ -1355,26 +1371,6 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Install test dependencies with overwritten environment variables.
-    CIBW_BEFORE_TEST: CC=gcc CXX=g++ pip install -r requirements.txt
-
-    # Chain commands using &&
-    CIBW_BEFORE_TEST: rm -rf ./data/cache && mkdir -p ./data/cache
-
-    # Install non pip python package
-    CIBW_BEFORE_TEST: >
-      cd some_dir &&
-      ./configure &&
-      make &&
-      make install
-
-    # Install python packages that are required to install test dependencies
-    CIBW_BEFORE_TEST: pip install cmake scikit-build
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -1404,8 +1400,28 @@ Platform-specific environment variables are also available:<br/>
 
     In configuration files, you can use an array, and the items will be joined with `&&`.
 
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Install test dependencies with overwritten environment variables.
+    CIBW_BEFORE_TEST: CC=gcc CXX=g++ pip install -r requirements.txt
+
+    # Chain commands using &&
+    CIBW_BEFORE_TEST: rm -rf ./data/cache && mkdir -p ./data/cache
+
+    # Install non pip python package
+    CIBW_BEFORE_TEST: >
+      cd some_dir &&
+      ./configure &&
+      make &&
+      make install
+
+    # Install python packages that are required to install test dependencies
+    CIBW_BEFORE_TEST: pip install cmake scikit-build
+    ```
+
 
-### `CIBW_TEST_SOURCES` {: #test-sources}
+### `test-sources` {: #test-sources env-var toml}
 > Files and folders from the source tree that are copied into an isolated tree before running the tests
 
 A space-separated list of files and folders, relative to the root of the
@@ -1413,7 +1429,7 @@ project, required for running the tests. If specified, these files and folders
 will be copied into a temporary folder, and that temporary folder will be used
 as the working directory for running the test suite.
 
-The use of `CIBW_TEST_SOURCES` is *required* for iOS builds. This is because the
+The use of `test-sources` is *required* for iOS builds. This is because the
 simulator does not have access to the project directory, as it is not stored on
 the simulator device. On iOS, the files will be copied into the test application,
 rather than a temporary folder.
@@ -1423,13 +1439,6 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Copy the "tests" folder, plus "data/test-image.png" from the source folder to the test folder.
-    CIBW_TEST_SOURCES: tests data/test-image.png
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -1440,8 +1449,15 @@ Platform-specific environment variables are also available:<br/>
 
     In configuration files, you can use an array, and the items will be joined with a space.
 
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Copy the "tests" folder, plus "data/test-image.png" from the source folder to the test folder.
+    CIBW_TEST_SOURCES: tests data/test-image.png
+    ```
 
-### `CIBW_TEST_REQUIRES` {: #test-requires}
+
+### `test-requires` {: #test-requires env-var toml}
 > Install Python dependencies before running the tests
 
 Space-separated list of dependencies required for running the tests.
@@ -1451,20 +1467,10 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Install pytest before running CIBW_TEST_COMMAND
-    CIBW_TEST_REQUIRES: pytest
-
-    # Install specific versions of test dependencies
-    CIBW_TEST_REQUIRES: pytest==8.2.2 packaging==24.1
-    ```
-
 !!! tab examples "pyproject.toml"
 
     ```toml
-    # Install pytest before running CIBW_TEST_COMMAND
+    # Install pytest before running test-command
     [tool.cibuildwheel]
     test-requires = "pytest"
 
@@ -1475,15 +1481,26 @@ Platform-specific environment variables are also available:<br/>
 
     In configuration files, you can use an array, and the items will be joined with a space.
 
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Install pytest before running CIBW_TEST_COMMAND
+    CIBW_TEST_REQUIRES: pytest
+
+    # Install specific versions of test dependencies
+    CIBW_TEST_REQUIRES: pytest==8.2.2 packaging==24.1
+    ```
+
 
-### `CIBW_TEST_EXTRAS` {: #test-extras}
+
+### `test-extras` {: #test-extras env-var toml}
 > Install your wheel for testing using `extras_require`
 
 List of
 [extras_require](https://setuptools.pypa.io/en/latest/userguide/dependency_management.html#declaring-required-dependency)
 options that should be included when installing the wheel prior to running the
 tests. This can be used to avoid having to redefine test dependencies in
-`CIBW_TEST_REQUIRES` if they are already defined in `pyproject.toml`,
+`test-requires` if they are already defined in `pyproject.toml`,
 `setup.cfg` or `setup.py`.
 
 Platform-specific environment variables are also available:<br/>
@@ -1491,15 +1508,6 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Will cause the wheel to be installed with `pip install <wheel_file>[test,qt]`
-    CIBW_TEST_EXTRAS: "test,qt"
-    ```
-
-    Separate multiple items with a comma.
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -1510,30 +1518,31 @@ Platform-specific environment variables are also available:<br/>
 
     In configuration files, you can use an inline array, and the items will be joined with a comma.
 
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Will cause the wheel to be installed with `pip install <wheel_file>[test,qt]`
+    CIBW_TEST_EXTRAS: "test,qt"
+    ```
+
+    Separate multiple items with a comma.
+
+
 
-### `CIBW_TEST_GROUPS` {: #test-groups}
+### `test-groups` {: #test-groups env-var toml}
 > Specify test dependencies from your project's `dependency-groups`
 
 List of
 [dependency-groups](https://peps.python.org/pep-0735)
 that should be included when installing the wheel prior to running the
 tests. This can be used to avoid having to redefine test dependencies in
-`CIBW_TEST_REQUIRES` if they are already defined in `pyproject.toml`.
+`test-requires` if they are already defined in `pyproject.toml`.
 
 Platform-specific environment variables are also available:<br/>
 `CIBW_TEST_GROUPS_MACOS` | `CIBW_TEST_GROUPS_WINDOWS` | `CIBW_TEST_GROUPS_LINUX` | `CIBW_TEST_GROUPS_PYODIDE`
 
 #### Examples
 
-!!! tab examples "Environment variables"
-
-    ```yaml
-    # Will cause the wheel to be installed with these groups of dependencies
-    CIBW_TEST_GROUPS: "test qt"
-    ```
-
-    Separate multiple items with a space.
-
 !!! tab examples "pyproject.toml"
 
     ```toml
@@ -1544,10 +1553,20 @@ Platform-specific environment variables are also available:<br/>
 
     In configuration files, you can use an inline array, and the items will be joined with a space.
 
-### `CIBW_TEST_SKIP` {: #test-skip}
+
+!!! tab examples "Environment variables"
+
+    ```yaml
+    # Will cause the wheel to be installed with these groups of dependencies
+    CIBW_TEST_GROUPS: "test qt"
+    ```
+
+    Separate multiple items with a space.
+
+### `test-skip` {: #test-skip env-var toml}
 > Skip running tests on some builds
 
-This will skip testing on any identifiers that match the given skip patterns (see [`CIBW_SKIP`](#build-skip)). This can be used to mask out tests for wheels that have missing dependencies upstream that are slow or hard to build, or to skip slow tests on emulated architectures.
+This will skip testing on any identifiers that match the given skip patterns (see [`skip`](#build-skip)). This can be used to mask out tests for wheels that have missing dependencies upstream that are slow or hard to build, or to skip slow tests on emulated architectures.
 
 With macOS `universal2` wheels, you can also skip the individual archs inside the wheel using an `:arch` suffix. For example, `cp39-macosx_universal2:x86_64` or `cp39-macosx_universal2:arm64`.
 
@@ -1555,66 +1574,73 @@ This option is not supported in the overrides section in `pyproject.toml`.
 
 #### Examples
 
-!!! tab examples "Environment variables"
+!!! tab examples "pyproject.toml"
 
-    ```yaml
+    ```toml
+    [tool.cibuildwheel]
     # Will avoid testing on emulated architectures
-    CIBW_TEST_SKIP: "*-*linux_{aarch64,ppc64le,s390x,armv7l}"
+    test-skip = "*-*linux_{aarch64,ppc64le,s390x,armv7l}"
 
     # Skip trying to test arm64 builds on Intel Macs
-    CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64"
+    test-skip = "*-macosx_arm64 *-macosx_universal2:arm64"
     ```
 
-!!! tab examples "pyproject.toml"
+!!! tab examples "Environment variables"
 
-    ```toml
-    [tool.cibuildwheel]
+    ```yaml
     # Will avoid testing on emulated architectures
-    test-skip = "*-*linux_{aarch64,ppc64le,s390x,armv7l}"
+    CIBW_TEST_SKIP: "*-*linux_{aarch64,ppc64le,s390x,armv7l}"
 
     # Skip trying to test arm64 builds on Intel Macs
-    test-skip = "*-macosx_arm64 *-macosx_universal2:arm64"
+    CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64"
     ```
 
-### `CIBW_TEST_ENVIRONMENT` {: #test-environment}
+
+### `test-environment` {: #test-environment toml env-var }
 
 > Set environment variables for the test environment
 
 A space-separated list of environment variables to set in the test environment.
 
-The syntax is the same as for [`CIBW_ENVIRONMENT`](#environment).
+The syntax is the same as for [`environment`](#environment).
 
-cibuildwheel sets the variable [`PYTHONSAFEPATH`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSAFEPATH) to avoid picking up in-tree dependencies when running the tests - we want to test the installed wheel, not the in-tree version. However, this can be overridden by setting `PYTHONSAFEPATH` to an empty string.
+cibuildwheel sets the variable
+[`PYTHONSAFEPATH`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSAFEPATH)
+to avoid picking up in-tree dependencies when running the tests - we want to
+test the installed wheel, not the in-tree version. However, this can be
+overridden by setting `PYTHONSAFEPATH` to an empty string.
 
 Platform-specific environment variables are also available:<br/>
 `CIBW_TEST_ENVIRONMENT_MACOS` | `CIBW_TEST_ENVIRONMENT_WINDOWS` | `CIBW_TEST_ENVIRONMENT_LINUX` | `CIBW_TEST_ENVIRONMENT_IOS` | `CIBW_TEST_ENVIRONMENT_PYODIDE`
 
 #### Examples
 
-!!! tab examples "Environment variables"
+!!! tab examples "pyproject.toml"
 
-    ```yaml
+    ```toml
+    [tool.cibuildwheel]
     # Set the environment variable MY_ENV_VAR to "my_value" in the test environment
-    CIBW_TEST_ENVIRONMENT: MY_ENV_VAR=my_value
+    test-environment = { MY_ENV_VAR="my_value" }
 
     # Unset PYTHONSAFEPATH in the test environment
-    CIBW_TEST_ENVIRONMENT: PYTHONSAFEPATH=
+    test-environment = { PYTHONSAFEPATH="" }
     ```
 
-!!! tab examples "pyproject.toml"
+!!! tab examples "Environment variables"
 
-    ```toml
-    [tool.cibuildwheel]
+    ```yaml
     # Set the environment variable MY_ENV_VAR to "my_value" in the test environment
-    test-environment = { MY_ENV_VAR="my_value" }
+    CIBW_TEST_ENVIRONMENT: MY_ENV_VAR=my_value
 
     # Unset PYTHONSAFEPATH in the test environment
-    test-environment = { PYTHONSAFEPATH="" }
+    CIBW_TEST_ENVIRONMENT: PYTHONSAFEPATH=
     ```
 
+
 ## Debugging
 
-### `CIBW_DEBUG_KEEP_CONTAINER`
+### `debug-keep-container` {: #debug-keep-container env-var}
+> Keep the container after running for debugging.
 
 Enable this flag to keep the container around for inspection after a build. This
 option is provided for debugging purposes only.
@@ -1630,7 +1656,7 @@ Default: Off (0).
 export CIBW_DEBUG_KEEP_CONTAINER=TRUE
 ```
 
-### `CIBW_DEBUG_TRACEBACK`
+### `debug-traceback` {: #debug-traceback cmd-line env-var}
 > Print full traceback when errors occur.
 
 Print a full traceback for the cibuildwheel process when errors occur. This
@@ -1644,7 +1670,7 @@ This option can also be set using the [command-line option](#command-line) `--de
 export CIBW_DEBUG_TRACEBACK=TRUE
 ```
 
-### `CIBW_BUILD_VERBOSITY` {: #build-verbosity}
+### `build-verbosity` {: #build-verbosity env-var toml}
 > Increase/decrease the output of the build
 
 This setting controls `-v`/`-q` flags to the build frontend. Since there is
@@ -1672,6 +1698,14 @@ Platform-specific environment variables are also available:<br/>
 
 #### Examples
 
+!!! tab examples "pyproject.toml"
+
+    ```toml
+    [tool.cibuildwheel]
+    # Ensure that the build backend output is present
+    build-verbosity = 1
+    ```
+
 !!! tab examples "Environment variables"
 
     ```yaml
@@ -1679,13 +1713,7 @@ Platform-specific environment variables are also available:<br/>
     CIBW_BUILD_VERBOSITY: 1
     ```
 
-!!! tab examples "pyproject.toml"
 
-    ```toml
-    [tool.cibuildwheel]
-    # Ensure that the build backend output is present
-    build-verbosity = 1
-    ```
 
 
 ## Command line {: #command-line}
@@ -1712,6 +1740,7 @@ Specific error codes are defined:
 Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, that are substituted by cibuildwheel before they are used. If, for some reason, you need to write the literal name of a placeholder, e.g. literally `{project}` in a command that would ordinarily substitute `{project}`, prefix it with a hash character - `#{project}`. This is only necessary in commands where the specific string between the curly brackets would be substituted - otherwise, strings not modified.
 
 <style>
+  /* Table of contents styling */
   .options-toc {
     display: grid;
     grid-template-columns: fit-content(20%) 1fr;
@@ -1742,8 +1771,64 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
   .options-toc a.option code {
     font-size: 80%;
   }
-  h3 code {
-    font-size: 100%;
+
+  /* header styling, including the badges */
+  .rst-content h3 code {
+    font-size: 115%;
+  }
+  .rst-content h3 .badges {
+    display: inline-flex;
+    justify-content: right;
+    flex-wrap: wrap;
+    flex-direction: row;
+    float: right;
+    width: fit-content;
+    max-width: 100%;
+    gap: 2px 2px;
+    position: relative;
+    top: 2px;
+  }
+  .rst-content h3 .badges code.cmd-line, .rst-content h3 .badges code.toml, .rst-content h3 .badges code.env-var {
+    font-size: 80%;
+    display: inline-flex;
+    flex-direction: column;
+    justify-content: left;
+    padding-left: 10px;
+    padding-right: 10px;
+    line-height: normal;
+  }
+  .rst-content h3 .badges code.cmd-line:before, .rst-content h3 .badges code.toml:before, .rst-content h3 .badges code.env-var:before {
+    content: ' ';
+    font-size: 10px;
+    font-weight: bold;
+    opacity: 0.5;
+    display: inline-block;
+    border-radius: 5px;
+    margin-left: -3px;
+    margin-right: -3px;
+    margin-bottom: -1px;
+  }
+  .rst-content h3 .badges code.cmd-line:before {
+    content: 'command line';
+  }
+  .rst-content h3 .badges code.cmd-line {
+    background: rgba(224, 202, 56, 0.3);
+  }
+  .rst-content h3 .badges code.toml:before {
+    content: 'pyproject.toml';
+  }
+  .rst-content h3 .badges code.toml {
+    background: rgba(41, 128, 185, 0.3);
+  }
+  .rst-content h3 .badges code.env-var:before {
+    content: 'env var';
+  }
+  .rst-content h3 .badges code.env-var {
+    background: rgba(61, 153, 112, 0.3);
+  }
+  /* sidebar TOC styling */
+  .toctree-l3.option a {
+    font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
   }
 </style>
 
@@ -1754,20 +1839,21 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
     var headers = []
 
     $('.rst-content h3')
-      .filter(function (i, el) {
-        return !!$(el).text().match(/(^([A-Z0-9, _*]| and )+)¶$/);
-      })
       .each(function (i, el) {
         var optionName = $(el).text().replace('¶', '');
         var description = $(el).next('blockquote').text()
         var header = $(el).prevAll('h2').first().text().replace('¶', '')
         var id = el.id;
 
+        if (optionName[0].match(/[A-Z]/)) {
+          // all the options are kebab-case, so this header isn't an option
+          return;
+        }
+
         if (options[header] === undefined) {
           options[header] = [];
           headers.push(header);
         }
-        console.log(optionName, description, header);
 
         options[header].push({name: optionName, description, id});
       });
@@ -1835,7 +1921,58 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
         markdown += '|\n'
       }
     }
-
     console.log('readme options markdown\n', markdown)
+
+    // add the option tags to each heading
+    $('.rst-content h3')
+      .each(function (i, el) {
+        el.classList.add('option', 'clearfix');
+        var optionName = $(el).text().replace('¶', '');
+
+        var cmdLine = el.getAttribute('cmd-line');
+        var envVar = el.getAttribute('env-var');
+        var toml = el.getAttribute('toml');
+
+        if (!(cmdLine || envVar || toml)) {
+          return;
+        }
+
+        var badgesEl = $('<div class="badges">')
+          .appendTo(el);
+
+        // fill default value
+        if (cmdLine == "cmd-line") {
+          cmdLine = '--'+optionName;
+        }
+        if (envVar == "env-var") {
+          envVar = optionName
+            .split(', ')
+            .map(opt => 'CIBW_'+opt.toUpperCase().replace(/-/g, '_'))
+            .join(', ');
+        }
+        if (toml == "toml") {
+          toml = optionName
+        }
+
+        if (toml) {
+          badgesEl.append(' <code class="toml" title="TOML option key">'+toml+'</code>');
+        }
+        if (cmdLine) {
+          badgesEl.append(' <code class="cmd-line" title="Command line argument">'+cmdLine+'</code>');
+        }
+        if (envVar) {
+          badgesEl.append(' <code class="env-var" title="Environment variable">'+envVar+'</code>');
+        }
+      });
+
+    $('.toctree-l3')
+      .each(function (i, el) {
+        var tocEntryName = $(el).text()
+        var isOption = tocEntryName[0].match(/^[a-z]/);
+        if (isOption) {
+          $(el).addClass('option');
+        }
+      });
   });
+
 </script>
diff --git a/docs/setup.md b/docs/setup.md
index c13fb972f..03a13491b 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -23,7 +23,24 @@ cibuildwheel
 !!!tip
     You can pass the `--platform linux` option to cibuildwheel to build Linux wheels, even if you're not on Linux. On most machines, the easiest builds to try are the Linux builds. You don't need any software installed except a Docker daemon, such as [Docker Desktop](https://www.docker.com/get-started/). Each platform that cibuildwheel supports has its own system requirements and platform-specific behaviors. See the [platforms page](platforms.md) for details.
 
-You should see the builds taking place. You can experiment with [options](options.md) using environment variables or pyproject.toml.
+You should see the builds taking place. You can experiment with [options](options.md) using pyproject.toml or environment variables.
+
+!!! tab "pyproject.toml"
+
+    If you write your options into [`pyproject.toml`](configuration.md#configuration-file), you can work on your options locally, and they'll be automatically picked up when running in CI.
+
+    > pyproject.toml
+
+    ```
+    [tool.cibuildwheel]
+    before-all = "uname -a"
+    ```
+
+    Then invoke cibuildwheel, like:
+
+    ```console
+    cibuildwheel
+    ```
 
 !!! tab "Environment variables"
 
@@ -46,23 +63,6 @@ You should see the builds taking place. You can experiment with [options](option
     cibuildwheel
     ```
 
-!!! tab "pyproject.toml"
-
-    If you write your options into [`pyproject.toml`](configuration.md#configuration-file), you can work on your options locally, and they'll be automatically picked up when running in CI.
-
-    > pyproject.toml
-
-    ```
-    [tool.cibuildwheel]
-    before-all = "uname -a"
-    ```
-
-    Then invoke cibuildwheel, like:
-
-    ```console
-    cibuildwheel
-    ```
-
 - Once you've got a build working locally, you can move on to [setting up a CI service](ci-services.md).
 - View the [full options reference](options.md) to see what cibuildwheel can do.
 - Check out the [FAQ](faq.md) for common questions.

From b1d944060a188fc2d33c3c9080e2881a11669384 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 26 May 2025 09:42:05 -0400
Subject: [PATCH 082/193] [Bot] Update dependencies (#2415)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |   6 +-
 cibuildwheel/resources/nodejs.toml            |   2 +-
 .../resources/pinned_docker_images.cfg        |  54 +--
 .../python-build-standalone-releases.json     | 434 +++++++++---------
 docs/working-examples.md                      |   8 +-
 5 files changed, 260 insertions(+), 244 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index e9116769d..5586115d0 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -54,7 +54,7 @@ platformdirs==4.3.8
     # via
     #   pyodide-build
     #   virtualenv
-pydantic==2.11.4
+pydantic==2.11.5
     # via
     #   pyodide-build
     #   pyodide-lock
@@ -81,7 +81,7 @@ rich==14.0.0
     #   pyodide-build
     #   pyodide-cli
     #   typer
-ruamel-yaml==0.18.10
+ruamel-yaml==0.18.11
     # via pyodide-build
 ruamel-yaml-clib==0.2.12
     # via ruamel-yaml
@@ -101,7 +101,7 @@ typing-extensions==4.13.2
     #   pydantic-core
     #   typer
     #   typing-inspection
-typing-inspection==0.4.0
+typing-inspection==0.4.1
     # via pydantic
 unearth==0.17.5
     # via pyodide-build
diff --git a/cibuildwheel/resources/nodejs.toml b/cibuildwheel/resources/nodejs.toml
index fc3fd2168..2897eac28 100644
--- a/cibuildwheel/resources/nodejs.toml
+++ b/cibuildwheel/resources/nodejs.toml
@@ -1,3 +1,3 @@
 url = "https://nodejs.org/dist/"
-v22 = "v22.15.1"
+v22 = "v22.16.0"
 v20 = "v20.19.2"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 20956fba4..b137183d2 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.16-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.16-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.16-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.24-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.24-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.24-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.24-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.16-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.24-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.24-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.16-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.16-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.16-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.24-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.24-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.24-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.24-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.16-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.16-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.16-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.24-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.24-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.24-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.24-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.16-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.16-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.16-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.24-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.24-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.24-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.24-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.16-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.16-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.24-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.24-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.24-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.24-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.16-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.16-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.16-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.24-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.24-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.24-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.16-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.16-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.24-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.24-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 407b0318c..bcbc196b7 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,423 +1,439 @@
 {
   "releases": [
     {
-      "tag": "20250409",
+      "tag": "20250517",
       "assets": [
         {
-          "name": "cpython-3.10.17+20250409-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.10.17%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.11.12%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.12.10%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a6+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.14.0a6%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.9.22%2B20250409-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.22+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index facc03ba0..40631cddb 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -52,8 +52,8 @@ title: Working examples
 | [PyYAML][]                        | ![github icon][] | ![apple icon][] | Canonical source repository for PyYAML |
 | [pikepdf][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python library for reading and writing PDF, powered by QPDF |
 | [numexpr][]                       | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast numerical array expression evaluator for Python, NumPy, Pandas, PyTables and more |
-| [h5py][]                          | ![azurepipelines icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | HDF5 for Python -- The h5py package is a Pythonic interface to the HDF5 binary data format. |
 | [Wrapt][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python module for decorators, wrappers and monkey patching. |
+| [h5py][]                          | ![azurepipelines icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | HDF5 for Python -- The h5py package is a Pythonic interface to the HDF5 binary data format. |
 | [envd][]                          | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A machine learning development environment build tool |
 | [Psycopg 3][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A modern implementation of a PostgreSQL adapter for Python |
 | [OpenColorIO][]                   | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. |
@@ -111,8 +111,8 @@ title: Working examples
 | [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
 | [ril][]                           | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
 | [SiPM][]                          | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
-| [GSD][]                           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
 | [aalink][]                        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
+| [GSD][]                           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
 | [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
 | [xmlstarlet][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. |
 | [werpy][]                         | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | An ultra-fast python package using optimized dynamic programming to compute the Word Error Rate (WER). |
@@ -162,8 +162,8 @@ title: Working examples
 [PyYAML]: https://github.com/yaml/pyyaml
 [pikepdf]: https://github.com/pikepdf/pikepdf
 [numexpr]: https://github.com/pydata/numexpr
-[h5py]: https://github.com/h5py/h5py
 [Wrapt]: https://github.com/GrahamDumpleton/wrapt
+[h5py]: https://github.com/h5py/h5py
 [envd]: https://github.com/tensorchord/envd
 [Psycopg 3]: https://github.com/psycopg/psycopg
 [OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO
@@ -221,8 +221,8 @@ title: Working examples
 [pyjet]: https://github.com/scikit-hep/pyjet
 [ril]: https://github.com/Cryptex-github/ril-py
 [SiPM]: https://github.com/EdoPro98/SimSiPM
-[GSD]: https://github.com/glotzerlab/gsd
 [aalink]: https://github.com/artfwo/aalink
+[GSD]: https://github.com/glotzerlab/gsd
 [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
 [xmlstarlet]: https://github.com/dimitern/xmlstarlet
 [werpy]: https://github.com/analyticsinmotion/werpy

From 2d6b40d66904a82ab87912e70d50ba9fd30519ab Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 26 May 2025 21:49:59 +0200
Subject: [PATCH 083/193] fix: rename `gp242` identifier to `gp311_242` (#2411)

---
 README.md                                   | 28 ++++++++++-----------
 bin/update_pythons.py                       |  4 +++
 cibuildwheel/logger.py                      |  5 +++-
 cibuildwheel/resources/build-platforms.toml | 10 ++++----
 cibuildwheel/selector.py                    |  2 +-
 cibuildwheel/util/packaging.py              |  1 +
 docs/options.md                             |  2 +-
 test/test_abi_variants.py                   |  2 +-
 unit_test/build_selector_test.py            |  3 ++-
 unit_test/option_prepare_test.py            |  6 ++---
 10 files changed, 36 insertions(+), 27 deletions(-)

diff --git a/README.md b/README.md
index 2de5577d5..8d260da70 100644
--- a/README.md
+++ b/README.md
@@ -23,20 +23,20 @@ What does it do?
 
 While cibuildwheel itself requires a recent Python version to run (we support the last three releases), it can target the following versions to build wheels:
 
-|                | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | iOS | Pyodide |
-|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|-----|
-| CPython 3.8    | ✅ | ✅  | ✅  | ✅  | N/A | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.9    | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅ | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.10   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.11   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.12   | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | ✅⁴ |
-| CPython 3.13³  | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
-| CPython 3.14³  | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
-| PyPy 3.8 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| PyPy 3.9 v7.3  | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| PyPy 3.10 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| PyPy 3.11 v7.3 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| GraalPy 24.2   | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | N/A  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+|                    | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | iOS | Pyodide |
+|--------------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|-----|
+| CPython 3.8        | ✅ | ✅  | ✅  | ✅  | N/A | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.9        | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅ | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.10       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.11       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
+| CPython 3.12       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | ✅⁴ |
+| CPython 3.13³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
+| CPython 3.14³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
+| PyPy 3.8 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.9 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.10 v7.3     | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.11 v7.3     | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+| GraalPy 3.11 v24.2 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | N/A  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
 
 <sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br>
 <sup>² Windows arm64 support is experimental.</sup><br>
diff --git a/bin/update_pythons.py b/bin/update_pythons.py
index 60b99f2aa..fa346281f 100755
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -147,7 +147,11 @@ def update_version(self, identifier: str, spec: Specifier) -> AnyConfig:
             msg = f"{identifier} not supported yet on GraalPy"
             raise RuntimeError(msg)
 
+        gpspec_str = identifier.split("-")[0].split("_")[1]
+        gpspec = Specifier("==24.2.*") if gpspec_str == "242" else Specifier(f"=={gpspec_str}.*")
+
         releases = [r for r in self.releases if spec.contains(r["python_version"])]
+        releases = [r for r in self.releases if gpspec.contains(r["graalpy_version"])]
         releases = sorted(releases, key=lambda r: r["graalpy_version"])
 
         if not releases:
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index 80c2fb02e..cee94d666 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -237,7 +237,8 @@ def build_description_from_identifier(identifier: str) -> str:
     build_description = ""
 
     python_interpreter = python_identifier[0:2]
-    python_version = python_identifier[2:]
+    version_parts = python_identifier[2:].split("_")
+    python_version = version_parts[0]
 
     if python_interpreter == "cp":
         build_description += "CPython"
@@ -250,6 +251,8 @@ def build_description_from_identifier(identifier: str) -> str:
         raise Exception(msg)
 
     build_description += f" {python_version[0]}.{python_version[1:]} "
+    if len(version_parts) > 1:
+        build_description += f"(ABI {version_parts[1]}) "
 
     try:
         build_description += PLATFORM_IDENTIFIER_DESCRIPTIONS[platform_identifier]
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 2875f9f24..10af09281 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -22,7 +22,7 @@ python_configurations = [
   { identifier = "pp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
   { identifier = "pp311-manylinux_x86_64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
-  { identifier = "gp242-manylinux_x86_64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
+  { identifier = "gp311_242-manylinux_x86_64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
   { identifier = "cp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
   { identifier = "cp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -72,7 +72,7 @@ python_configurations = [
   { identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
   { identifier = "pp311-manylinux_aarch64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
-  { identifier = "gp242-manylinux_aarch64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
+  { identifier = "gp311_242-manylinux_aarch64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
   { identifier = "pp38-manylinux_i686", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
   { identifier = "pp39-manylinux_i686", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
   { identifier = "pp310-manylinux_i686", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
@@ -179,8 +179,8 @@ python_configurations = [
   { identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
   { identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_x86_64.tar.bz2" },
   { identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_arm64.tar.bz2" },
-  { identifier = "gp242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-amd64.tar.gz" },
-  { identifier = "gp242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-aarch64.tar.gz" },
+  { identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-amd64.tar.gz" },
+  { identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-aarch64.tar.gz" },
 ]
 
 [windows]
@@ -215,7 +215,7 @@ python_configurations = [
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
   { identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-win64.zip" },
-  { identifier = "gp242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-windows-amd64.zip" },
+  { identifier = "gp311_242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-windows-amd64.zip" },
 ]
 
 [pyodide]
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index c3f9dea4b..25f61176e 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -75,7 +75,7 @@ class BuildSelector:
     def __call__(self, build_id: str) -> bool:
         # Filter build selectors by python_requires if set
         if self.requires_python is not None:
-            py_ver_str = build_id.split("-")[0]
+            py_ver_str = build_id.split("-")[0].split("_")[0]
             py_ver_str = py_ver_str.removesuffix("t")
             major = int(py_ver_str[2])
             minor = int(py_ver_str[3:])
diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py
index 310778198..b13ebc6bc 100644
--- a/cibuildwheel/util/packaging.py
+++ b/cibuildwheel/util/packaging.py
@@ -135,6 +135,7 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
     """
 
     interpreter, platform = identifier.split("-", 1)
+    interpreter = interpreter.split("_")[0]
     free_threaded = interpreter.endswith("t")
     if free_threaded:
         interpreter = interpreter[:-1]
diff --git a/docs/options.md b/docs/options.md
index 3ade43d93..89e782bf5 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -64,7 +64,7 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
 | PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
 | PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
 | PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
-| GraalPy v24.2 | gp242-macosx_x86_64<br/>gp242-macosx_arm64                             | gp242-win_amd64                                     | gp242-manylinux_x86_64                                                                              | gp242-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
+| GraalPy 3.11 v24.2 | gp311_242-macosx_x86_64<br/>gp311_242-macosx_arm64                             | gp311_242-win_amd64                                     | gp311_242-manylinux_x86_64                                                                              | gp311_242-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
 
 The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
 The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
index d3734ae64..f64f42485 100644
--- a/test/test_abi_variants.py
+++ b/test/test_abi_variants.py
@@ -41,7 +41,7 @@ def test_abi3(tmp_path):
         add_env={
             # free_threaded, GraalPy, and PyPy do not have a Py_LIMITED_API equivalent, just build one of those
             # also limit the number of builds for test performance reasons
-            "CIBW_BUILD": "cp39-* cp310-* pp310-* gp242-* cp312-* cp313t-*",
+            "CIBW_BUILD": "cp39-* cp310-* pp310-* gp311_242-* cp312-* cp313t-*",
             "CIBW_ENABLE": "all",
         },
     )
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index 6da657fca..1c87c4264 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -152,7 +152,7 @@ def test_build_limited_python():
         build_config="*",
         skip_config="",
         requires_python=SpecifierSet(">=3.7"),
-        enable=frozenset([EnableGroup.PyPy, EnableGroup.PyPyEoL]),
+        enable=frozenset([EnableGroup.PyPy, EnableGroup.PyPyEoL, EnableGroup.GraalPy]),
     )
 
     assert not build_selector("cp36-manylinux_x86_64")
@@ -165,6 +165,7 @@ def test_build_limited_python():
     assert build_selector("cp37-win32")
     assert build_selector("cp38-win32")
     assert build_selector("pp37-win_amd64")
+    assert build_selector("gp311_242-win_amd64")
 
 
 def test_build_limited_python_partial():
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 2f87044ad..34f26eee6 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -14,7 +14,7 @@
 from cibuildwheel.util import file
 
 DEFAULT_IDS = {"cp38", "cp39", "cp310", "cp311", "cp312", "cp313"}
-ALL_IDS = DEFAULT_IDS | {"cp313t", "pp38", "pp39", "pp310", "pp311", "gp242"}
+ALL_IDS = DEFAULT_IDS | {"cp313t", "pp38", "pp39", "pp310", "pp311", "gp311_242"}
 
 
 @pytest.fixture
@@ -157,7 +157,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
             "pp39",
             "pp310",
             "pp311",
-            "gp242",
+            "gp311_242",
         }
     }
     assert kwargs["options"].build_options("cp39-manylinux_x86_64").before_all == ""
@@ -179,7 +179,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
             "pp39",
             "pp310",
             "pp311",
-            "gp242",
+            "gp311_242",
         ]
     }
 

From 2b4a8535d91d25a9d108e1bef35505d7c93e34fe Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Tue, 27 May 2025 11:42:21 +0800
Subject: [PATCH 084/193] fix: ensure that deep paths exist before copying
 files. (#2418)

---
 cibuildwheel/oci_container.py   |  1 +
 unit_test/oci_container_test.py | 13 ++++++++++---
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py
index fa3d77048..ebfe38fca 100644
--- a/cibuildwheel/oci_container.py
+++ b/cibuildwheel/oci_container.py
@@ -342,6 +342,7 @@ def copy_into(self, from_path: Path, to_path: PurePath) -> None:
             )
         else:
             exec_process: subprocess.Popen[bytes]
+            self.call(["mkdir", "-p", to_path.parent])
             with subprocess.Popen(
                 [
                     self.engine.name,
diff --git a/unit_test/oci_container_test.py b/unit_test/oci_container_test.py
index c6ea3b645..373144a1b 100644
--- a/unit_test/oci_container_test.py
+++ b/unit_test/oci_container_test.py
@@ -239,16 +239,23 @@ def test_binary_output(container_engine):
         assert output == binary_data_string
 
 
-def test_file_operation(tmp_path: Path, container_engine: OCIContainerEngineConfig) -> None:
+@pytest.mark.parametrize(
+    "file_path",
+    ["test.dat", "path/to/test.dat"],
+)
+def test_file_operation(
+    tmp_path: Path, container_engine: OCIContainerEngineConfig, file_path: str
+) -> None:
     with OCIContainer(
         engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
     ) as container:
         # test copying a file in
         test_binary_data = bytes(random.randrange(256) for _ in range(1000))
-        original_test_file = tmp_path / "test.dat"
+        original_test_file = tmp_path / file_path
+        original_test_file.parent.mkdir(parents=True, exist_ok=True)
         original_test_file.write_bytes(test_binary_data)
 
-        dst_file = PurePath("/tmp/test.dat")
+        dst_file = PurePath("/tmp") / file_path
 
         container.copy_into(original_test_file, dst_file)
 

From 3bbc772778a3745a594b02f62c7c9f00378d850a Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 26 May 2025 23:42:40 -0400
Subject: [PATCH 085/193] [pre-commit.ci] pre-commit autoupdate (#2417)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.10 → v0.11.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.10...v0.11.11)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2304b31fe..22315221d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.10
+  rev: v0.11.11
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From e92147f52a3f32818588e968f4be9315c8c35635 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Tue, 27 May 2025 04:44:39 +0100
Subject: [PATCH 086/193] [travis-ci] debugging ci failures on travis (#2387)

* [travis-ci] debugging ci failures on travis

* [travis-ci] Use JSON to encode env vars

* [travis-ci] Use a more broadly compatible before_all command

* Drop CIBW_ENABLE=all on Travis windows
It doesn't fit into the time limit

* [travis-ci] try upgrading certifi to get around SSL errors

* Revert "[travis-ci] try upgrading certifi to get around SSL errors"

This reverts commit 02eee0e1946d2116d1ce23ccd86945ad33d1ae24.

* [travis-ci] upgrade windows certificates

* [travis-ci] re-enable linux builds

* Drop CIBW_ENABLE=all on Travis Linux

It doesn't fit into the time limit

---------

Co-authored-by: mayeut <mayeut@users.noreply.github.com>
---
 .travis.yml                       | 10 +++++++--
 cibuildwheel/platforms/windows.py | 37 +++++++++++++------------------
 test/test_before_all.py           | 13 ++++++-----
 3 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index e4d1d72a3..d2082cc1f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,8 @@ jobs:
       services: docker
       env:
         - PYTHON=python
-        - CIBW_ENABLE=all
+        # a build using CIBW_ENABLE=all does not fit into Travis' time limit,
+        # so only the defaults are tested
 
     - name: Linux | arm64 | Python 3.12
       python: 3.12
@@ -42,10 +43,15 @@ jobs:
       os: windows
       language: shell
       before_install:
+        # http://woshub.com/updating-trusted-root-certificates-in-windows-10
+        - certutil -generateSSTFromWU roots.sst
+        - powershell -Command 'Get-ChildItem -Path roots.sst | Import-Certificate -CertStoreLocation Cert:\LocalMachine\Root'
+        - rm -f roots.sst
         - choco upgrade python3 -y --version 3.12.8 --limit-output --params "/InstallDir:C:\\Python312"
       env:
         - PYTHON=C:\\Python312\\python
-        - CIBW_ENABLE=all
+        # a build using CIBW_ENABLE=all does not fit into Travis' time limit,
+        # so only the defaults are tested
 
     - name: Linux | s390x | Python 3.12
       python: 3.12
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 4e59275bf..52e081f76 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -1,3 +1,4 @@
+import json
 import os
 import platform as platform_module
 import shutil
@@ -350,28 +351,22 @@ def setup_python(
             text=True,
         ).strip()
         log.notice(f"Discovering Visual Studio for GraalPy at {vcpath}")
-        env.update(
-            dict(
-                [
-                    envvar.strip().split("=", 1)
-                    for envvar in subprocess.check_output(
-                        [
-                            f"{vcpath}\\Common7\\Tools\\vsdevcmd.bat",
-                            "-no_logo",
-                            "-arch=amd64",
-                            "-host_arch=amd64",
-                            "&&",
-                            "set",
-                        ],
-                        shell=True,
-                        text=True,
-                        env=env,
-                    )
-                    .strip()
-                    .split("\n")
-                ]
-            )
+        vcvars = subprocess.check_output(
+            [
+                f"{vcpath}\\Common7\\Tools\\vsdevcmd.bat",
+                "-no_logo",
+                "-arch=amd64",
+                "-host_arch=amd64",
+                "&&",
+                "python",
+                "-c",
+                "import os, json, sys; json.dump(dict(os.environ), sys.stdout);",
+            ],
+            shell=True,
+            text=True,
+            env=env,
         )
+        env.update(json.loads(vcvars))
 
     return base_python, env
 
diff --git a/test/test_before_all.py b/test/test_before_all.py
index c7ec86240..f0c89972b 100644
--- a/test/test_before_all.py
+++ b/test/test_before_all.py
@@ -36,16 +36,19 @@ def test(tmp_path):
     with (project_dir / "text_info.txt").open(mode="w") as ff:
         print("dummy text", file=ff)
 
-    # build the wheels
+    # write python version information to a temporary file, this is checked in
+    # setup.py
+    #
+    # note, before_all runs in whatever the host environment is, `python`
+    # might be any version of python (even Python 2 on Travis ci!), so this is
+    # written to be broadly compatible
     before_all_command = (
-        """python -c "import os, pathlib, sys; pathlib.Path('{project}/text_info.txt').write_text('sample text '+os.environ.get('TEST_VAL', ''))" && """
-        '''python -c "import pathlib, sys; pathlib.Path('{project}/python_prefix.txt').write_text(sys.prefix)"'''
+        """python -c "import os, sys; f = open('{project}/text_info.txt', 'w'); f.write('sample text '+os.environ.get('TEST_VAL', '')); f.close()" && """
+        '''python -c "import sys; f = open('{project}/python_prefix.txt', 'w'); f.write(sys.prefix); f.close()"'''
     )
     actual_wheels = utils.cibuildwheel_run(
         project_dir,
         add_env={
-            # write python version information to a temporary file, this is
-            # checked in setup.py
             "CIBW_BEFORE_ALL": before_all_command,
             "CIBW_BEFORE_ALL_LINUX": f'{before_all_command} && python -c "import sys; assert sys.version_info >= (3, 8)"',
             "CIBW_ENVIRONMENT": "TEST_VAL='123'",

From b8814b814884040e7b66bd8e20a4820dfe8c825b Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 27 May 2025 19:32:14 -0400
Subject: [PATCH 087/193] chore: fix update script to handle pymanager addition
 to page (#2424)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 bin/update_pythons.py | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/bin/update_pythons.py b/bin/update_pythons.py
index fa346281f..d1058f452 100755
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -280,8 +280,12 @@ def __init__(self) -> None:
 
         self.versions_dict: dict[Version, int] = {}
         for release in releases_info:
-            # Removing the prefix, Python 3.9 would use: release["name"].removeprefix("Python ")
-            version = Version(release["name"][7:])
+            # Skip the pymanager releases
+            if not release["slug"].startswith("python"):
+                continue
+
+            # Removing the prefix
+            version = Version(release["name"].removeprefix("Python "))
 
             uri = int(release["resource_uri"].rstrip("/").split("/")[-1])
             self.versions_dict[version] = uri

From e18ab66cca43fc698bc90380cd2062b3a95571b7 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 27 May 2025 19:32:25 -0400
Subject: [PATCH 088/193] fix: pyodide missing wheel placeholder (#2421)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/platforms/pyodide.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index dc6b5c6a4..d4ee9a921 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -498,6 +498,7 @@ def build(options: Options, tmp_path: Path) -> None:
                         build_options.before_test,
                         project=".",
                         package=build_options.package_dir,
+                        wheel=repaired_wheel,
                     )
                     shell(before_test_prepared, env=virtualenv_env)
 

From db2d59be22f774aea1a52534918bd12b3d370965 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 28 May 2025 05:41:26 -0400
Subject: [PATCH 089/193] chore: use kw_only (Python 3.10+) on most dataclasses
 (#2422)

* chore: use kw_only (Python 3.10+) on many dataclasses

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: expose windows file to type checker

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 bin/update_docker.py              |  4 ++--
 bin/update_nodejs.py              |  4 ++--
 bin/update_virtualenv.py          |  4 ++--
 cibuildwheel/bashlex_eval.py      |  4 ++--
 cibuildwheel/environment.py       |  2 +-
 cibuildwheel/frontend.py          |  4 ++--
 cibuildwheel/oci_container.py     |  7 +++---
 cibuildwheel/options.py           |  6 ++---
 cibuildwheel/platforms/ios.py     |  4 ++--
 cibuildwheel/platforms/linux.py   |  6 ++---
 cibuildwheel/platforms/macos.py   |  4 ++--
 cibuildwheel/platforms/pyodide.py |  4 ++--
 cibuildwheel/platforms/windows.py |  4 ++--
 cibuildwheel/selector.py          |  6 ++---
 cibuildwheel/util/packaging.py    |  2 +-
 unit_test/get_platform_test.py    | 38 +++++++++++++++++++++----------
 16 files changed, 59 insertions(+), 44 deletions(-)

diff --git a/bin/update_docker.py b/bin/update_docker.py
index efb90154e..3c6ef79ed 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 
 import configparser
-from dataclasses import dataclass
+import dataclasses
 from pathlib import Path
 
 import requests
@@ -11,7 +11,7 @@
 RESOURCES = DIR.parent / "cibuildwheel/resources"
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True)
 class Image:
     manylinux_version: str
     platforms: list[str]
diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py
index 3f9eb5260..a564fad00 100755
--- a/bin/update_nodejs.py
+++ b/bin/update_nodejs.py
@@ -1,10 +1,10 @@
 #!/usr/bin/env python3
 
 
+import dataclasses
 import difflib
 import logging
 import tomllib
-from dataclasses import dataclass
 from pathlib import Path
 from typing import Final
 
@@ -27,7 +27,7 @@
 NODEJS_INDEX: Final[str] = f"{NODEJS_DIST}index.json"
 
 
-@dataclass(frozen=True, order=True)
+@dataclasses.dataclass(frozen=True, order=True)
 class VersionTuple:
     version: Version
     version_string: str
diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py
index c6cd1b0cb..6e38eadb6 100755
--- a/bin/update_virtualenv.py
+++ b/bin/update_virtualenv.py
@@ -1,11 +1,11 @@
 #!/usr/bin/env python3
 
 
+import dataclasses
 import difflib
 import logging
 import subprocess
 import tomllib
-from dataclasses import dataclass
 from pathlib import Path
 from typing import Final
 
@@ -28,7 +28,7 @@
 )
 
 
-@dataclass(frozen=True, order=True)
+@dataclasses.dataclass(frozen=True, order=True)
 class VersionTuple:
     version: Version
     version_string: str
diff --git a/cibuildwheel/bashlex_eval.py b/cibuildwheel/bashlex_eval.py
index 6656d0a4d..3d1ac1185 100644
--- a/cibuildwheel/bashlex_eval.py
+++ b/cibuildwheel/bashlex_eval.py
@@ -1,3 +1,4 @@
+import dataclasses
 import subprocess
 from collections.abc import (
     Callable,
@@ -5,7 +6,6 @@
     Mapping,
     Sequence,
 )
-from dataclasses import dataclass
 
 import bashlex
 
@@ -17,7 +17,7 @@ def local_environment_executor(command: Sequence[str], env: Mapping[str, str]) -
     return subprocess.run(command, env=env, text=True, stdout=subprocess.PIPE, check=True).stdout
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class NodeExecutionContext:
     environment: dict[str, str]
     input: str
diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py
index d5a0796d1..a9a3f8627 100644
--- a/cibuildwheel/environment.py
+++ b/cibuildwheel/environment.py
@@ -102,7 +102,7 @@ def __eq__(self, other: object) -> bool:
         return False
 
 
-@dataclasses.dataclass
+@dataclasses.dataclass(kw_only=True)
 class ParsedEnvironment:
     assignments: list[EnvironmentAssignment]
 
diff --git a/cibuildwheel/frontend.py b/cibuildwheel/frontend.py
index 79796a1be..18157cdd0 100644
--- a/cibuildwheel/frontend.py
+++ b/cibuildwheel/frontend.py
@@ -1,7 +1,7 @@
+import dataclasses
 import shlex
 import typing
 from collections.abc import Sequence
-from dataclasses import dataclass
 from typing import Literal, Self, get_args
 
 from .logger import log
@@ -10,7 +10,7 @@
 BuildFrontendName = Literal["pip", "build", "build[uv]"]
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True)
 class BuildFrontendConfig:
     name: BuildFrontendName
     args: Sequence[str] = ()
diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py
index ebfe38fca..59a1ca7e2 100644
--- a/cibuildwheel/oci_container.py
+++ b/cibuildwheel/oci_container.py
@@ -1,3 +1,4 @@
+import dataclasses
 import io
 import json
 import os
@@ -10,7 +11,6 @@
 import typing
 import uuid
 from collections.abc import Mapping, Sequence
-from dataclasses import dataclass, field
 from enum import Enum
 from pathlib import Path, PurePath, PurePosixPath
 from types import TracebackType
@@ -37,10 +37,11 @@ class OCIPlatform(Enum):
     S390X = "linux/s390x"
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True)
 class OCIContainerEngineConfig:
     name: ContainerEngineName
-    create_args: tuple[str, ...] = field(default_factory=tuple)
+    _: dataclasses.KW_ONLY
+    create_args: tuple[str, ...] = dataclasses.field(default_factory=tuple)
     disable_host_mount: bool = False
 
     @classmethod
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 5a9c96c25..7c98f1e21 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -51,7 +51,7 @@
 )
 
 
-@dataclasses.dataclass
+@dataclasses.dataclass(kw_only=True)
 class CommandLineArguments:
     platform: Literal["auto", "linux", "macos", "windows"] | None
     archs: str | None
@@ -80,7 +80,7 @@ def defaults(cls) -> Self:
         )
 
 
-@dataclasses.dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class GlobalOptions:
     package_dir: Path
     output_dir: Path
@@ -90,7 +90,7 @@ class GlobalOptions:
     allow_empty: bool
 
 
-@dataclasses.dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class BuildOptions:
     globals: GlobalOptions
     environment: ParsedEnvironment
diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index dc4911f75..1235d92d6 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import dataclasses
 import os
 import shlex
 import shutil
@@ -7,7 +8,6 @@
 import sys
 import textwrap
 from collections.abc import Sequence, Set
-from dataclasses import dataclass
 from pathlib import Path
 from typing import assert_never
 
@@ -42,7 +42,7 @@
 from .macos import install_cpython as install_build_cpython
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class PythonConfiguration:
     version: str
     identifier: str
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 5ffd5a2ef..f31529cb8 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -1,10 +1,10 @@
 import contextlib
+import dataclasses
 import subprocess
 import sys
 import textwrap
 from collections import OrderedDict
 from collections.abc import Iterable, Iterator, Sequence, Set
-from dataclasses import dataclass
 from pathlib import Path, PurePath, PurePosixPath
 from typing import assert_never
 
@@ -32,7 +32,7 @@
 }
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class PythonConfiguration:
     version: str
     identifier: str
@@ -43,7 +43,7 @@ def path(self) -> PurePosixPath:
         return PurePosixPath(self.path_str)
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class BuildStep:
     platform_configs: list[PythonConfiguration]
     platform_tag: str
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index d64917368..1975c6633 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -1,3 +1,4 @@
+import dataclasses
 import functools
 import inspect
 import os
@@ -8,7 +9,6 @@
 import sys
 import typing
 from collections.abc import Set
-from dataclasses import dataclass
 from pathlib import Path
 from typing import Literal, assert_never
 
@@ -76,7 +76,7 @@ def get_macos_sdks() -> list[str]:
     return [m.group(1) for m in re.finditer(r"-sdk (macosx\S+)", output)]
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class PythonConfiguration:
     version: str
     identifier: str
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index d4ee9a921..3159acd64 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -1,3 +1,4 @@
+import dataclasses
 import functools
 import json
 import os
@@ -6,7 +7,6 @@
 import tomllib
 import typing
 from collections.abc import Set
-from dataclasses import dataclass
 from pathlib import Path
 from tempfile import TemporaryDirectory
 from typing import Final, TypedDict
@@ -41,7 +41,7 @@
 IS_WIN: Final[bool] = sys.platform.startswith("win")
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class PythonConfiguration:
     version: str
     identifier: str
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 52e081f76..ce2d312d1 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -1,3 +1,4 @@
+import dataclasses
 import json
 import os
 import platform as platform_module
@@ -5,7 +6,6 @@
 import subprocess
 import textwrap
 from collections.abc import MutableMapping, Set
-from dataclasses import dataclass
 from functools import cache
 from pathlib import Path
 from typing import assert_never
@@ -51,7 +51,7 @@ def get_nuget_args(
     ]
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class PythonConfiguration:
     version: str
     arch: str
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 25f61176e..42f4104be 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -1,5 +1,5 @@
+import dataclasses
 import itertools
-from dataclasses import dataclass
 from enum import StrEnum
 from fnmatch import fnmatch
 from typing import Any
@@ -58,7 +58,7 @@ def parse_option_value(cls, value: str) -> frozenset["EnableGroup"]:
         return frozenset(result)
 
 
-@dataclass(frozen=True, kw_only=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class BuildSelector:
     """
     This class holds a set of build/skip patterns. You call an instance with a
@@ -113,7 +113,7 @@ def options_summary(self) -> Any:
         }
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True, kw_only=True)
 class TestSelector:
     """
     A build selector that can only skip tests according to a skip pattern.
diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py
index b13ebc6bc..295bc091c 100644
--- a/cibuildwheel/util/packaging.py
+++ b/cibuildwheel/util/packaging.py
@@ -11,7 +11,7 @@
 from .helpers import parse_key_value_string, unwrap
 
 
-@dataclass()
+@dataclass(kw_only=True)
 class DependencyConstraints:
     base_file_path: Path | None = None
     packages: list[str] = field(default_factory=list)
diff --git a/unit_test/get_platform_test.py b/unit_test/get_platform_test.py
index 9bd46cd99..320b73ae7 100644
--- a/unit_test/get_platform_test.py
+++ b/unit_test/get_platform_test.py
@@ -1,6 +1,8 @@
 import contextlib
 import sys
+from collections.abc import Generator
 from pathlib import Path
+from typing import TYPE_CHECKING
 
 import pytest
 import setuptools._distutils.util
@@ -10,23 +12,27 @@
 from cibuildwheel.platforms.windows import PythonConfiguration, setup_setuptools_cross_compile
 
 # monkeypatching os.name is too flaky. E.g. It works on my machine, but fails in pipeline
-if not sys.platform.startswith("win"):
+if not sys.platform.startswith("win") and not TYPE_CHECKING:
     pytest.skip("Windows-only tests", allow_module_level=True)
 
 
 @contextlib.contextmanager
-def patched_environment(monkeypatch: pytest.MonkeyPatch, environment: dict[str, str]):
+def patched_environment(
+    monkeypatch: pytest.MonkeyPatch, environment: dict[str, str]
+) -> Generator[None, None, None]:
     with monkeypatch.context() as mp:
         for envvar, val in environment.items():
             mp.setenv(name=envvar, value=val)
         yield
 
 
-def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
+def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
     arch = "32"
     environment: dict[str, str] = {}
 
-    configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None)
+    configuration = PythonConfiguration(
+        version="irrelevant", arch=arch, identifier="irrelevant", url=None
+    )
 
     setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
     with patched_environment(monkeypatch, environment):
@@ -36,11 +42,13 @@ def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
     assert target_platform == "win32"
 
 
-def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
+def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
     arch = "64"
     environment: dict[str, str] = {}
 
-    configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None)
+    configuration = PythonConfiguration(
+        version="irrelevant", arch=arch, identifier="irrelevant", url=None
+    )
 
     setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
     with patched_environment(monkeypatch, environment):
@@ -53,11 +61,13 @@ def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
 @pytest.mark.skipif(
     detect_ci_provider() == CIProvider.azure_pipelines, reason="arm64 not recognised on azure"
 )
-def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
+def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
     arch = "ARM64"
     environment: dict[str, str] = {}
 
-    configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None)
+    configuration = PythonConfiguration(
+        version="irrelevant", arch=arch, identifier="irrelevant", url=None
+    )
 
     setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
     with patched_environment(monkeypatch, environment):
@@ -67,21 +77,25 @@ def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
     assert target_platform == "win-arm64"
 
 
-def test_env_set(tmp_path: Path):
+def test_env_set(tmp_path: Path) -> None:
     arch = "32"
     environment = {"VSCMD_ARG_TGT_ARCH": "x64"}
 
-    configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None)
+    configuration = PythonConfiguration(
+        version="irrelevant", arch=arch, identifier="irrelevant", url=None
+    )
 
     with pytest.raises(FatalError, match="VSCMD_ARG_TGT_ARCH"):
         setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
 
 
-def test_env_blank(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
+def test_env_blank(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
     arch = "32"
     environment = {"VSCMD_ARG_TGT_ARCH": ""}
 
-    configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None)
+    configuration = PythonConfiguration(
+        version="irrelevant", arch=arch, identifier="irrelevant", url=None
+    )
 
     setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment)
     with patched_environment(monkeypatch, environment):

From 7298563b2557ef4f236a679e03183f465b5a2bb9 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 28 May 2025 16:04:55 +0100
Subject: [PATCH 090/193] docs: fix many/musllinux option names and values and
 other broken links (#2425)

---
 docs/configuration.md   |  2 +-
 docs/deliver-to-pypi.md |  2 +-
 docs/faq.md             |  4 ++--
 docs/options.md         | 30 +++++++++++++++---------------
 docs/platforms.md       |  2 +-
 5 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index 0a0ce985f..ae5ffc334 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -66,7 +66,7 @@ cibuildwheel to run tests, add the following YAML to your CI config file:
 
 !!! tab "Gitlab CI"
 
-    > .gitlab-ci.yml ([docs](https://docs.gitlab.com/ee/ci/variables/README.html#create-a-custom-variable-in-gitlab-ciyml))
+    > .gitlab-ci.yml ([docs](https://docs.gitlab.com/ci/yaml/#variables))
 
     ```yaml
     linux:
diff --git a/docs/deliver-to-pypi.md b/docs/deliver-to-pypi.md
index 6453ff6f8..edd59b384 100644
--- a/docs/deliver-to-pypi.md
+++ b/docs/deliver-to-pypi.md
@@ -79,7 +79,7 @@ for an example configuration.
 
 ## Manual method
 
-On your development machine, install [pipx](https://pypa.github.io/pipx/) and do the following:
+On your development machine, install [pipx](https://pipx.pypa.io/) and do the following:
 
 ```bash
 # Either download the SDist from your CI, or make it:
diff --git a/docs/faq.md b/docs/faq.md
index 42e38c4bb..e132413b0 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -163,7 +163,7 @@ setuptools.setup(
 
 Your build might need some compiler flags to be set through environment variables.
 Consider incorporating these into your package, for example, in `setup.py` using [`extra_compile_args` or
-`extra_link_args`](https://docs.python.org/3/distutils/setupscript.html#other-options).
+`extra_link_args`](https://setuptools.pypa.io/en/latest/userguide/ext_modules.html#setuptools.Extension).
 
 ## Troubleshooting
 
@@ -313,7 +313,7 @@ This error may happen when you install a library using a package manager like Ho
 
 ### Windows: 'ImportError: DLL load failed: The specific module could not be found'
 
-Visual Studio and MSVC link the compiled binary wheels to the Microsoft Visual C++ Runtime. Normally, the C parts of the runtime are included with Python, but the C++ components are not. When compiling modules using C++, it is possible users will run into problems on systems that do not have the full set of runtime libraries installed. The solution is to ask users to download the corresponding Visual C++ Redistributable from the [Microsoft website](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) and install it.
+Visual Studio and MSVC link the compiled binary wheels to the Microsoft Visual C++ Runtime. Normally, the C parts of the runtime are included with Python, but the C++ components are not. When compiling modules using C++, it is possible users will run into problems on systems that do not have the full set of runtime libraries installed. The solution is to ask users to download the corresponding Visual C++ Redistributable from the [Microsoft website](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist) and install it.
 
 Additionally, Visual Studio 2019 started linking to an even newer DLL, `VCRUNTIME140_1.dll`, besides the `VCRUNTIME140.dll` that is included with recent Python versions (starting from Python 3.5; see [here](https://wiki.python.org/moin/WindowsCompilers) for more details on the corresponding Visual Studio & MSVC versions used to compile the different Python versions). To avoid this extra dependency on `VCRUNTIME140_1.dll`, the [`/d2FH4-` flag](https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/) can be added to the MSVC invocations (check out [this issue](https://github.com/pypa/cibuildwheel/issues/423) for details and references). CPython 3.8.3 and all versions after it have this extra DLL, so it is only needed for 3.8 and earlier.
 
diff --git a/docs/options.md b/docs/options.md
index 89e782bf5..1a49a03e3 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -982,22 +982,22 @@ The available options are:
 
 | Option                         | Default                                                         |
 |--------------------------------|-----------------------------------------------------------------|
-| `manylinux-x86-64-image`       | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-x86-64)  |
-| `manylinux-i686-image`         | [`manylinux2014`](https://quay.io/pypa/manylinux2014-i686)      |
-| `manylinux-pypy-x86-64-image`  | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-x86-64)  |
-| `manylinux-aarch64-image`      | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-aarch64) |
-| `manylinux-ppc64le-image`      | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-ppc64le) |
-| `manylinux-s390x-image`        | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-s390x)   |
-| `manylinux-armv7l-image`       | [`manylinux-2-31`](https://quay.io/pypa/manylinux-2-31-armv7l)  |
+| `manylinux_x86_64-image`       | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
+| `manylinux-i686-image`         | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
+| `manylinux-pypy_x86_64-image`  | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
+| `manylinux-aarch64-image`      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
+| `manylinux-ppc64le-image`      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
+| `manylinux-s390x-image`        | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x)   |
+| `manylinux-armv7l-image`       | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l)  |
 | `manylinux-riscv64-image`      | No default                                                      |
-| `manylinux-pypy-aarch64-image` | [`manylinux-2-28`](https://quay.io/pypa/manylinux-2-28-aarch64) |
-| `manylinux-pypy-i686-image`    | [`manylinux2014`](https://quay.io/pypa/manylinux2014-i686)      |
-| `musllinux-x86-64-image`       | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-x86-64)    |
-| `musllinux-i686-image`         | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-i686)      |
-| `musllinux-aarch64-image`      | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-aarch64)   |
-| `musllinux-ppc64le-image`      | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-ppc64le)   |
-| `musllinux-s390x-image`        | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-s390x)     |
-| `musllinux-armv7l-image`       | [`musllinux-1-2`](https://quay.io/pypa/musllinux-1-2-armv7l)    |
+| `manylinux-pypy_aarch64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
+| `manylinux-pypy_i686-image`    | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
+| `musllinux_x86_64-image`       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64)    |
+| `musllinux-i686-image`         | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686)      |
+| `musllinux-aarch64-image`      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64)   |
+| `musllinux-ppc64le-image`      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le)   |
+| `musllinux-s390x-image`        | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x)     |
+| `musllinux-armv7l-image`       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l)    |
 | `musllinux-riscv64-image`      | No default                                                      |
 
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
diff --git a/docs/platforms.md b/docs/platforms.md
index c4ffb9af6..ca9c0f90c 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -152,7 +152,7 @@ In order to speed-up builds, cibuildwheel will cache the tools it needs to be re
 
 `cibuildwheel` supports cross-compiling `ARM64` wheels on all Windows runners, but a native ARM64 runner is required for testing. On non-native runners, tests for ARM64 wheels will be automatically skipped with a warning. Add `"*-win_arm64"` to your `CIBW_TEST_SKIP` setting to suppress the warning.
 
-Cross-compilation on Windows relies on a supported build backend. Supported backends use an environment variable to specify their target platform (the one they are compiling native modules for, as opposed to the one they are running on), which is set in [cibuildwheels/windows.py](https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/windows.py) before building. Currently, `setuptools>=65.4.1` and `setuptools_rust` are the only supported backends.
+Cross-compilation on Windows relies on a supported build backend. Supported backends use an environment variable to specify their target platform (the one they are compiling native modules for, as opposed to the one they are running on), which is set in [cibuildwheel's windows.py](https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/platforms/windows.py) before building. Currently, `setuptools>=65.4.1` and `setuptools_rust` are the only supported backends.
 
 By default, `ARM64` is not enabled when running on non-ARM64 runners. Use [`CIBW_ARCHS`](options.md#archs) to select it.
 

From 63797729570cef6f8a55bec6b331c1e8107abf0d Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 28 May 2025 16:05:16 +0100
Subject: [PATCH 091/193] Revert to running tests from from a temp dir when
 test-sources is unset (#2420)

* Revert to running tests from from a temp dir when test-sources is unset

* Fix placeholders error message, add test for it

* Add back {project} placeholders to CIBW_TEST_COMMAND in tests & docs

* Update test/test_before_test.py

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
---
 cibuildwheel/platforms/ios.py                 | 42 +++++++++----
 cibuildwheel/platforms/linux.py               | 10 +--
 cibuildwheel/platforms/macos.py               | 11 +++-
 cibuildwheel/platforms/pyodide.py             | 10 +--
 cibuildwheel/platforms/windows.py             | 22 ++++---
 .../resources/testing_temp_dir_file.py        | 36 +++++++++++
 cibuildwheel/util/resources.py                |  1 +
 docs/configuration.md                         | 23 ++-----
 docs/options.md                               | 46 +++++++-------
 test/test_abi_variants.py                     |  2 +-
 test/test_before_test.py                      |  4 +-
 test/test_emulation.py                        |  2 +-
 test/test_ios.py                              | 41 +++++++++++-
 test/test_macos_archs.py                      |  2 -
 test/test_pyodide.py                          |  2 +-
 test/test_testing.py                          | 62 +++++++++++--------
 16 files changed, 209 insertions(+), 107 deletions(-)
 create mode 100644 cibuildwheel/resources/testing_temp_dir_file.py

diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 1235d92d6..660af67c1 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -559,19 +559,19 @@ def build(options: Options, tmp_path: Path) -> None:
                         env=test_env,
                     )
 
-                    if not build_options.test_sources:
-                        # iOS requires an explicit test-sources, as the project directory
-                        # isn't visible on the simulator.
-
-                        msg = "Testing on iOS requires a definition of test-sources."
-                        raise errors.FatalError(msg)
+                    testbed_app_path = testbed_path / "iOSTestbed" / "app"
 
                     # Copy the test sources to the testbed app
-                    copy_test_sources(
-                        build_options.test_sources,
-                        build_options.package_dir,
-                        testbed_path / "iOSTestbed" / "app",
-                    )
+                    if build_options.test_sources:
+                        copy_test_sources(
+                            build_options.test_sources,
+                            build_options.package_dir,
+                            testbed_app_path,
+                        )
+                    else:
+                        (testbed_app_path / "test_fail.py").write_text(
+                            resources.TEST_FAIL_CWD_FILE.read_text()
+                        )
 
                     log.step("Installing test requirements...")
                     # Install the compiled wheel (with any test extras), plus
@@ -598,6 +598,26 @@ def build(options: Options, tmp_path: Path) -> None:
 
                     log.step("Running test suite...")
 
+                    # iOS doesn't support placeholders in the test command,
+                    # because the source dir isn't visible on the simulator.
+                    if (
+                        "{project}" in build_options.test_command
+                        or "{package}" in build_options.test_command
+                    ):
+                        msg = unwrap_preserving_paragraphs(
+                            f"""
+                            iOS tests configured with a test command that uses the "{{project}}" or
+                            "{{package}}" placeholder. iOS tests cannot use placeholders, because the
+                            source directory is not visible on the simulator.
+
+                            In addition, iOS tests must run as a Python module, so the test command
+                            must begin with 'python -m'.
+
+                            Test command: {build_options.test_command!r}
+                            """
+                        )
+                        raise errors.FatalError(msg)
+
                     test_command_parts = shlex.split(build_options.test_command)
                     if test_command_parts[0:2] != ["python", "-m"]:
                         first_part = test_command_parts[0]
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index f31529cb8..31f02cc85 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -398,9 +398,10 @@ def build_in_container(
                 wheel=wheel_to_test,
             )
 
+            test_cwd = testing_temp_dir / "test_cwd"
+            container.call(["mkdir", "-p", test_cwd])
+
             if build_options.test_sources:
-                test_cwd = testing_temp_dir / "test_cwd"
-                container.call(["mkdir", "-p", test_cwd])
                 copy_test_sources(
                     build_options.test_sources,
                     build_options.package_dir,
@@ -408,8 +409,9 @@ def build_in_container(
                     copy_into=container.copy_into,
                 )
             else:
-                # There are no test sources. Run the tests in the project directory.
-                test_cwd = PurePosixPath(container_project_path)
+                # Use the test_fail.py file to raise a nice error if the user
+                # tries to run tests in the cwd
+                container.copy_into(resources.TEST_FAIL_CWD_FILE, test_cwd / "test_fail.py")
 
             container.call(["sh", "-c", test_command_prepared], cwd=test_cwd, env=virtualenv_env)
 
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 1975c6633..9dae02e5f 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -706,8 +706,9 @@ def build(options: Options, tmp_path: Path) -> None:
                         wheel=repaired_wheel,
                     )
 
+                    test_cwd = identifier_tmp_dir / "test_cwd"
+
                     if build_options.test_sources:
-                        test_cwd = identifier_tmp_dir / "test_cwd"
                         # only create test_cwd if it doesn't already exist - it
                         # may have been created during a previous `testing_arch`
                         if not test_cwd.exists():
@@ -718,8 +719,12 @@ def build(options: Options, tmp_path: Path) -> None:
                                 test_cwd,
                             )
                     else:
-                        # There are no test sources. Run the tests in the project directory.
-                        test_cwd = Path.cwd()
+                        # Use the test_fail.py file to raise a nice error if the user
+                        # tries to run tests in the cwd
+                        test_cwd.mkdir(exist_ok=True)
+                        (test_cwd / "test_fail.py").write_text(
+                            resources.TEST_FAIL_CWD_FILE.read_text()
+                        )
 
                     shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
 
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index 3159acd64..fdabc3aa0 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -523,17 +523,19 @@ def build(options: Options, tmp_path: Path) -> None:
                     package=build_options.package_dir.resolve(),
                 )
 
+                test_cwd = identifier_tmp_dir / "test_cwd"
+                test_cwd.mkdir(exist_ok=True)
+
                 if build_options.test_sources:
-                    test_cwd = identifier_tmp_dir / "test_cwd"
-                    test_cwd.mkdir(exist_ok=True)
                     copy_test_sources(
                         build_options.test_sources,
                         build_options.package_dir,
                         test_cwd,
                     )
                 else:
-                    # There are no test sources. Run the tests in the project directory.
-                    test_cwd = Path.cwd()
+                    # Use the test_fail.py file to raise a nice error if the user
+                    # tries to run tests in the cwd
+                    (test_cwd / "test_fail.py").write_text(resources.TEST_FAIL_CWD_FILE.read_text())
 
                 shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
 
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index ce2d312d1..17375fad4 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -588,24 +588,26 @@ def build(options: Options, tmp_path: Path) -> None:
                 # run the tests from a temp dir, with an absolute path in the command
                 # (this ensures that Python runs the tests against the installed wheel
                 # and not the repo code)
-                test_command_prepared = prepare_command(
-                    build_options.test_command,
-                    project=Path.cwd(),
-                    package=options.globals.package_dir.resolve(),
-                    wheel=repaired_wheel,
-                )
+                test_cwd = identifier_tmp_dir / "test_cwd"
+                test_cwd.mkdir()
+
                 if build_options.test_sources:
-                    test_cwd = identifier_tmp_dir / "test_cwd"
-                    test_cwd.mkdir()
                     copy_test_sources(
                         build_options.test_sources,
                         build_options.package_dir,
                         test_cwd,
                     )
                 else:
-                    # There are no test sources. Run the tests in the project directory.
-                    test_cwd = Path.cwd()
+                    # Use the test_fail.py file to raise a nice error if the user
+                    # tries to run tests in the cwd
+                    (test_cwd / "test_fail.py").write_text(resources.TEST_FAIL_CWD_FILE.read_text())
 
+                test_command_prepared = prepare_command(
+                    build_options.test_command,
+                    project=Path.cwd(),
+                    package=options.globals.package_dir.resolve(),
+                    wheel=repaired_wheel,
+                )
                 shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
 
             # we're all done here; move it to output (remove if already exists)
diff --git a/cibuildwheel/resources/testing_temp_dir_file.py b/cibuildwheel/resources/testing_temp_dir_file.py
new file mode 100644
index 000000000..094f2f6fb
--- /dev/null
+++ b/cibuildwheel/resources/testing_temp_dir_file.py
@@ -0,0 +1,36 @@
+# this file is copied to the testing cwd, to raise the below error message if
+# pytest/unittest is run from there.
+
+import sys
+import unittest
+from typing import NoReturn
+
+
+class TestStringMethods(unittest.TestCase):
+    def test_fail(self) -> NoReturn:
+        if sys.platform == "ios":
+            msg = (
+                "You tried to run tests from the testbed app's working "
+                "directory, without specifying `test-sources`. "
+                "On iOS, you must copy your test files to the testbed app by "
+                "setting the `test-sources` option in your cibuildwheel "
+                "configuration."
+            )
+        else:
+            msg = (
+                "cibuildwheel executes tests from a different working directory to "
+                "your project. This ensures only your wheel is imported, preventing "
+                "Python from accessing files that haven't been packaged into the "
+                "wheel. "
+                "\n\n"
+                "Please specify a path to your tests when invoking pytest "
+                "using the {project} placeholder, e.g. `pytest {project}` or "
+                "`pytest {project}/tests`. cibuildwheel will replace {project} with "
+                "the path to your project. "
+                "\n\n"
+                "Alternatively, you can specify your test files using the "
+                "`test-sources` option, and cibuildwheel will copy them to the "
+                "working directory for testing."
+            )
+
+        self.fail(msg)
diff --git a/cibuildwheel/util/resources.py b/cibuildwheel/util/resources.py
index a3cef7815..0cb09a26d 100644
--- a/cibuildwheel/util/resources.py
+++ b/cibuildwheel/util/resources.py
@@ -17,6 +17,7 @@
 VIRTUALENV: Final[Path] = PATH / "virtualenv.toml"
 CIBUILDWHEEL_SCHEMA: Final[Path] = PATH / "cibuildwheel.schema.json"
 PYTHON_BUILD_STANDALONE_RELEASES: Final[Path] = PATH / "python-build-standalone-releases.json"
+TEST_FAIL_CWD_FILE: Final[Path] = PATH / "testing_temp_dir_file.py"
 
 
 # this value is cached because it's used a lot in unit tests
diff --git a/docs/configuration.md b/docs/configuration.md
index ae5ffc334..1f1736056 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -17,7 +17,7 @@ cibuildwheel to run tests, add the following YAML to your CI config file:
     ```yaml
     env:
       CIBW_TEST_REQUIRES: pytest
-      CIBW_TEST_COMMAND: "pytest ./tests"
+      CIBW_TEST_COMMAND: "pytest {project}/tests"
     ```
 
 !!! tab "Azure Pipelines"
@@ -27,7 +27,7 @@ cibuildwheel to run tests, add the following YAML to your CI config file:
     ```yaml
     variables:
       CIBW_TEST_REQUIRES: pytest
-      CIBW_TEST_COMMAND: "pytest ./tests"
+      CIBW_TEST_COMMAND: "pytest {project}/tests"
     ```
 
 !!! tab "Travis CI"
@@ -38,18 +38,7 @@ cibuildwheel to run tests, add the following YAML to your CI config file:
     env:
       global:
         - CIBW_TEST_REQUIRES=pytest
-        - CIBW_TEST_COMMAND="pytest ./tests"
-    ```
-
-!!! tab "AppVeyor"
-
-    > appveyor.yml ([docs](https://www.appveyor.com/docs/build-configuration/#environment-variables))
-
-    ```yaml
-    environment:
-      global:
-        CIBW_TEST_REQUIRES: pytest
-        CIBW_TEST_COMMAND: "pytest {project}\\tests"
+        - CIBW_TEST_COMMAND="pytest {project}/tests"
     ```
 
 !!! tab "CircleCI"
@@ -61,7 +50,7 @@ cibuildwheel to run tests, add the following YAML to your CI config file:
       job_name:
         environment:
           CIBW_TEST_REQUIRES: pytest
-          CIBW_TEST_COMMAND: "pytest ./tests"
+          CIBW_TEST_COMMAND: "pytest {project}/tests"
     ```
 
 !!! tab "Gitlab CI"
@@ -72,7 +61,7 @@ cibuildwheel to run tests, add the following YAML to your CI config file:
     linux:
       variables:
         CIBW_TEST_REQUIRES: pytest
-        CIBW_TEST_COMMAND: "pytest ./tests"
+        CIBW_TEST_COMMAND: "pytest {project}/tests"
     ```
 
 !!! tab "Cirrus CI"
@@ -82,7 +71,7 @@ cibuildwheel to run tests, add the following YAML to your CI config file:
     ```yaml
     env:
       CIBW_TEST_REQUIRES: pytest
-      CIBW_TEST_COMMAND: "pytest ./tests"
+      CIBW_TEST_COMMAND: "pytest {project}/tests"
     ```
 
 ## Configuration file {: #configuration-file}
diff --git a/docs/options.md b/docs/options.md
index 1a49a03e3..95a82e3db 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -28,7 +28,7 @@ This option can also be set using the [command-line option](#command-line) `--pl
 
     ```bash
     export CIBW_BUILD='cp37-*'
-    export CIBW_TEST_COMMAND='pytest ./tests'
+    export CIBW_TEST_COMMAND='pytest {project}/tests'
     cibuildwheel --platform linux .
     ```
 
@@ -1284,23 +1284,23 @@ Shell command to run tests after the build. The wheel will be installed
 automatically and available for import from the tests. If this variable is not
 set, your wheel will not be installed after building.
 
-By default, tests are executed from your project directory. When specifying
-`test-command`, you can optionally use the placeholders `{package}` and
-`{project}` to pass in the location of your test code:
+To ensure the wheel is imported by your tests (instead of your source copy),
+**Tests are executed from a temporary directory**, outside of your source
+tree. To access your test code, you have a couple of options:
 
-- `{package}` is the path to the package being built - the `package_dir`
-  argument supplied to cibuildwheel on the command line.
-- `{project}` is an absolute path to the project root - the working directory
-  where cibuildwheel was called.
+- You can use the [`test-sources`](#test-sources) setting to copy specific
+  files from your source tree into the temporary directory. When using
+  test-sources, use relative paths in your test command, as if they were
+  relative to the project root.
 
-Using `{package}` or `{project}` used to be required, but since cibuildwheel
-3.0, tests are run from the project root by default. This means that you can
-use relative paths in your test command, and they will be relative to the
-project root.
+- You can use the `{package}` or `{project}` placeholders in your
+  `test-command` to refer to the package being built or the project root,
+  respectively.
 
-Alternatively, you can use the [`test-sources`](#test-sources) setting to
-create a temporary folder populated with a specific subset of project files to
-run your test suite.
+    - `{package}` is the path to the package being built - the `package_dir`
+      argument supplied to cibuildwheel on the command line.
+    - `{project}` is an absolute path to the project root - the working
+      directory where cibuildwheel was called.
 
 On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
 
@@ -1318,18 +1318,18 @@ Platform-specific environment variables are also available:<br/>
     ```toml
     [tool.cibuildwheel]
     # Run the package tests using `pytest`
-    test-command = "pytest ./tests"
+    test-command = "pytest {project}/tests"
 
     # Trigger an install of the package, but run nothing of note
     test-command = "echo Wheel installed"
 
     # Multiline example
     test-command = [
-      "pytest ./tests",
-      "python ./test.py",
+      "pytest {project}/tests",
+      "python {project}/test.py",
     ]
 
-    # run tests on ios
+    # run tests on ios - when test-sources is set, use relative paths, not {project} or {package}
     [tool.cibuildwheel.ios]
     test-sources = ["tests"]
     test-command = "python -m pytest ./tests"
@@ -1341,17 +1341,17 @@ Platform-specific environment variables are also available:<br/>
 
     ```yaml
     # Run the package tests using `pytest`
-    CIBW_TEST_COMMAND: pytest ./tests
+    CIBW_TEST_COMMAND: pytest {project}/tests
 
     # Trigger an install of the package, but run nothing of note
     CIBW_TEST_COMMAND: "echo Wheel installed"
 
     # Multi-line example - join with && on all platforms
     CIBW_TEST_COMMAND: >
-      pytest ./tests &&
-      python ./test.py
+      pytest {project}/tests &&
+      python {project}/test.py
 
-    # run tests on ios
+    # run tests on ios - when test-sources is set, use relative paths, not {project} or {package}
     CIBW_TEST_SOURCES_IOS: tests
     CIBW_TEST_COMMAND_IOS: python -m pytest ./tests
     ```
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
index f64f42485..2a849f3a2 100644
--- a/test/test_abi_variants.py
+++ b/test/test_abi_variants.py
@@ -187,7 +187,7 @@ def test_abi_none(tmp_path, capfd):
         project_dir,
         add_env={
             "CIBW_TEST_REQUIRES": "pytest",
-            "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} {{project}}/test",
             # limit the number of builds for test performance reasons
             "CIBW_BUILD": "cp38-* cp{}{}-* cp313t-* pp310-*".format(*utils.SINGLE_PYTHON_VERSION),
             "CIBW_ENABLE": "all",
diff --git a/test/test_before_test.py b/test/test_before_test.py
index c6ed5a7bb..3c722d089 100644
--- a/test/test_before_test.py
+++ b/test/test_before_test.py
@@ -62,10 +62,10 @@ def test(tmp_path, build_frontend_env):
             "CIBW_TEST_REQUIRES": "pytest",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
             # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
             # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "where graalpy || pytest ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "where graalpy || pytest {project}/test",
             **build_frontend_env,
         },
     )
diff --git a/test/test_emulation.py b/test/test_emulation.py
index 9749d9da0..4a749cfa3 100644
--- a/test/test_emulation.py
+++ b/test/test_emulation.py
@@ -32,7 +32,7 @@ def test(tmp_path, request):
         project_dir,
         add_env={
             "CIBW_TEST_REQUIRES": "pytest",
-            "CIBW_TEST_COMMAND": "pytest ./test",
+            "CIBW_TEST_COMMAND": "pytest {project}/test",
             "CIBW_ARCHS": archs,
             # TODO remove me once proper support is added
             "CIBW_MANYLINUX_RISCV64_IMAGE": "ghcr.io/mayeut/manylinux_2_35:2025.05.11-1",
diff --git a/test/test_ios.py b/test/test_ios.py
index 39d54ae91..9ee588b26 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -91,8 +91,9 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
     assert "'does-exist' will be included in the cross-build environment" in captured.out
 
 
+@pytest.mark.serial
 def test_no_test_sources(tmp_path, capfd):
-    """Build will fail if test-sources isn't defined."""
+    """Build will provide a helpful error if pytest is run and test-sources is not defined."""
     if utils.get_platform() != "macos":
         pytest.skip("this test can only run on macOS")
     if utils.get_xcode_version() < (13, 0):
@@ -109,13 +110,47 @@ def test_no_test_sources(tmp_path, capfd):
             add_env={
                 "CIBW_PLATFORM": "ios",
                 "CIBW_BUILD": "cp313-*",
-                "CIBW_TEST_COMMAND": "python -m tests",
+                "CIBW_TEST_REQUIRES": "pytest",
+                "CIBW_TEST_COMMAND": "python -m pytest",
+                "CIBW_XBUILD_TOOLS": "",
+            },
+        )
+
+    # The error message indicates the configuration issue.
+    captured = capfd.readouterr()
+    assert (
+        "you must copy your test files to the testbed app by setting the `test-sources` option"
+        in captured.out + captured.err
+    )
+
+
+def test_ios_testing_with_placeholder(tmp_path, capfd):
+    """Build will run tests with the {project} placeholder."""
+    if utils.get_platform() != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+
+    project_dir = tmp_path / "project"
+    basic_project = test_projects.new_c_project()
+    basic_project.files.update(basic_project_files)
+    basic_project.generate(project_dir)
+
+    with pytest.raises(subprocess.CalledProcessError):
+        utils.cibuildwheel_run(
+            project_dir,
+            add_env={
+                "CIBW_PLATFORM": "ios",
+                "CIBW_BUILD": "cp313-*",
+                "CIBW_TEST_REQUIRES": "pytest",
+                "CIBW_TEST_COMMAND": "pytest {project}/tests",
+                "CIBW_XBUILD_TOOLS": "",
             },
         )
 
     # The error message indicates the configuration issue.
     captured = capfd.readouterr()
-    assert "Testing on iOS requires a definition of test-sources." in captured.err
+    assert "iOS tests cannot use placeholders" in captured.out + captured.err
 
 
 def test_missing_xbuild_tool(tmp_path, capfd):
diff --git a/test/test_macos_archs.py b/test/test_macos_archs.py
index f38cff567..f799e55b0 100644
--- a/test/test_macos_archs.py
+++ b/test/test_macos_archs.py
@@ -42,11 +42,9 @@ def test_cross_compiled_build(tmp_path):
 @pytest.mark.parametrize(
     "test_config",
     [
-        # Run the test suite in the project folder
         {
             "CIBW_TEST_COMMAND": '''python -c "import platform; print('running tests on ' + platform.machine())"''',
         },
-        # Nominate the set of test sources to copy
         {
             "CIBW_TEST_COMMAND": "python tests/test_suite.py",
             "CIBW_TEST_SOURCES": "tests",
diff --git a/test/test_pyodide.py b/test/test_pyodide.py
index e1dbbd56e..39bc2be32 100644
--- a/test/test_pyodide.py
+++ b/test/test_pyodide.py
@@ -129,7 +129,7 @@ def test_filter():
             add_args=["--platform", "pyodide"],
             add_env={
                 "CIBW_TEST_REQUIRES": "pytest",
-                "CIBW_TEST_COMMAND": "python -m pytest",
+                "CIBW_TEST_COMMAND": "python -m pytest {project}",
             },
         )
         # check that the expected wheels are produced
diff --git a/test/test_testing.py b/test/test_testing.py
index 88054c545..f12e7b5d5 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -86,10 +86,10 @@ def test(tmp_path):
             "CIBW_TEST_REQUIRES": "pytest",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
             # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
             # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest {project}/test",
         },
     )
 
@@ -109,10 +109,10 @@ def test_extras_require(tmp_path):
             "CIBW_TEST_EXTRAS": "test",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
             # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
             # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest {project}/test",
         },
         single_python=True,
     )
@@ -143,10 +143,10 @@ def test_dependency_groups(tmp_path):
             "CIBW_TEST_GROUPS": "dev",
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
-            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} ./test",
+            "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
             # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
             # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest ./test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest {project}/test",
         },
         single_python=True,
     )
@@ -178,7 +178,7 @@ def test_failing_test(tmp_path):
             output_dir=output_dir,
             add_env={
                 "CIBW_TEST_REQUIRES": "pytest",
-                "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} ./test",
+                "CIBW_TEST_COMMAND": f"{utils.invoke_pytest()} {{project}}/test",
                 # CPython 3.8 when running on macOS arm64 is unusual. The build
                 # always runs in x86_64, so the arm64 tests are not run. See
                 # #1169 for reasons why. That means the build succeeds, which
@@ -191,28 +191,40 @@ def test_failing_test(tmp_path):
 
 
 @pytest.mark.parametrize("test_runner", ["pytest", "unittest"])
-def test_bare_pytest_invocation(tmp_path: Path, test_runner: str) -> None:
-    """Check that if a user runs a bare test suite, it runs in the project folder"""
+def test_bare_pytest_invocation(
+    tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str
+) -> None:
+    """
+    Check that if a user runs pytest in the the test cwd without setting
+    test-sources, it raises a helpful error
+    """
     project_dir = tmp_path / "project"
     project_with_a_test.generate(project_dir)
+    output_dir = tmp_path / "output"
 
-    actual_wheels = utils.cibuildwheel_run(
-        project_dir,
-        add_env={
-            "CIBW_TEST_REQUIRES": "pytest" if test_runner == "pytest" else "",
-            "CIBW_TEST_COMMAND": (
-                # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
-                # until https://github.com/oracle/graalpython/issues/490 fixed
-                "graalpy.exe -c 1 || python -m pytest"
-                if test_runner == "pytest"
-                else "python -m unittest discover test spam_test.py"
-            ),
-        },
-    )
+    with pytest.raises(subprocess.CalledProcessError):
+        utils.cibuildwheel_run(
+            project_dir,
+            output_dir=output_dir,
+            add_env={
+                "CIBW_TEST_REQUIRES": "pytest" if test_runner == "pytest" else "",
+                "CIBW_TEST_COMMAND": (
+                    "python -m pytest" if test_runner == "pytest" else "python -m unittest"
+                ),
+                # Skip CPython 3.8 on macOS arm64, see comment above in
+                # 'test_failing_test'
+                "CIBW_SKIP": "cp38-macosx_arm64",
+            },
+        )
 
-    # check that we got the right wheels
-    expected_wheels = utils.expected_wheels("spam", "0.1.0")
-    assert set(actual_wheels) == set(expected_wheels)
+    assert len(list(output_dir.iterdir())) == 0
+
+    captured = capfd.readouterr()
+
+    assert (
+        "Please specify a path to your tests when invoking pytest using the {project} placeholder"
+        in captured.out + captured.err
+    )
 
 
 def test_test_sources(tmp_path):

From c41b47f7c2464ff7945b34e211ddc4b89f423a45 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 28 May 2025 20:07:38 +0100
Subject: [PATCH 092/193] Bump version: v3.0.0b3

---
 README.md                              | 25 +++++++++++++------------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      | 15 ++++++++++++---
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 50 insertions(+), 40 deletions(-)

diff --git a/README.md b/README.md
index 8d260da70..574a422e2 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0b2
+        run: python -m pip install cibuildwheel==3.0.0b3
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -223,8 +223,16 @@ Not yet released, but available for testing.
 
 Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
-Known issues:
-- ⚠️ The CWD for test-command has changed in v3.0, but that is still [being debated](https://github.com/pypa/cibuildwheel/issues/2406), it might change before the final release. Please provide feedback on the aforementioned issue if you do (or don't!) encounter issues with this.
+If you've used previous versions of the beta:
+- ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
+- ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
+
+#### v3.0.0b3
+
+_28 May 2025_
+
+- 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
+- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
 
 #### v3.0.0b2
 
@@ -245,7 +253,8 @@ _19 May 2025_
 - ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources), and changes the working directory for tests.
 
     - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into a temporary directory, and run the tests from there. This is required for iOS builds, but also useful for other platforms, as it allows you to test the installed wheel without any chance of accidentally importing from the source tree.
-    - If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).
+    - ~If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).~
+    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory.
 
 - ✨ Added `dependency-versions` inline syntax (#2123)
 - 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
@@ -260,14 +269,6 @@ _26 April 2025_
 
 - 🛠 Dependency updates, including Python 3.13.3 (#2371)
 
-### v2.23.2
-
-_24 March 2025_
-
-- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
-- 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
--
-
 <!-- END bin/update_readme_changelog.py -->
 
 ---
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index c9e418604..6ab976e35 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0b2"
+__version__ = "3.0.0b3"
diff --git a/docs/changelog.md b/docs/changelog.md
index 740f82b41..fce8d721a 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -10,8 +10,16 @@ Not yet released, but available for testing.
 
 Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
-Known issues:
-- ⚠️ The CWD for test-command has changed in v3.0, but that is still [being debated](https://github.com/pypa/cibuildwheel/issues/2406), it might change before the final release. Please provide feedback on the aforementioned issue if you do (or don't!) encounter issues with this.
+If you've used previous versions of the beta:
+- ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
+- ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
+
+#### v3.0.0b3
+
+_28 May 2025_
+
+- 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
+- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
 
 #### v3.0.0b2
 
@@ -32,7 +40,8 @@ _19 May 2025_
 - ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources), and changes the working directory for tests.
 
     - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into a temporary directory, and run the tests from there. This is required for iOS builds, but also useful for other platforms, as it allows you to test the installed wheel without any chance of accidentally importing from the source tree.
-    - If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).
+    - ~If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).~
+    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory.
 
 - ✨ Added `dependency-versions` inline syntax (#2123)
 - 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
diff --git a/docs/faq.md b/docs/faq.md
index e132413b0..9a04e216c 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0b2
+uses: pypa/cibuildwheel@v3.0.0b3
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0b2
+cibuildwheel==3.0.0b3
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0b2 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0b3 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index de5fee7a1..fa805c94f 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0b2
+        pip3 install cibuildwheel==3.0.0b3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0b2
+        python3 -m pip install cibuildwheel==3.0.0b3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0b2
+        pip install cibuildwheel==3.0.0b3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 45bd7ac90..aa27628a8 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b2
+            python3 -m pip install --user cibuildwheel==3.0.0b3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b2
+            python3 -m pip install --user cibuildwheel==3.0.0b3
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0b2
+            pip3 install cibuildwheel==3.0.0b3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index ddbfac6ff..3dd47a778 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b2
+    - python -m pip install cibuildwheel==3.0.0b3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index bc6a655aa..1688a1542 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b2
+    - python -m pip install cibuildwheel==3.0.0b3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index da63c0922..69b9e3037 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b2
+        uses: pypa/cibuildwheel@v3.0.0b3
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 838d5b395..7b08021a4 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b2
+        uses: pypa/cibuildwheel@v3.0.0b3
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index 166c6162a..a9643590c 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0b2
+        run: pipx run cibuildwheel==3.0.0b3
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 1db755101..32c6abdd2 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b2
+        uses: pypa/cibuildwheel@v3.0.0b3
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 420dce8cb..cdea6e172 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0b2
+    - python -m pip install cibuildwheel==3.0.0b3
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0b2
+    - py -m pip install cibuildwheel==3.0.0b3
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0b2
+    - python3 -m pip install cibuildwheel==3.0.0b3
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 937cd8f35..320a3788b 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0b2
+    - python -m pip install cibuildwheel==3.0.0b3
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index 976b55323..9210526c5 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b2
+  - python3 -m pip install cibuildwheel==3.0.0b3
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index d8018a40b..0645a1189 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b2
+  - python3 -m pip install cibuildwheel==3.0.0b3
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 3b6c211e0..a0e90f1f8 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0b2 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0b2 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index f08fda1b0..5840e43a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0b2"
+version = "3.0.0b3"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 7e099863cdbe9f0c241aa5ef52747f2db6b6f310 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 28 May 2025 20:38:53 +0100
Subject: [PATCH 093/193] docs: update options table in README and keep
 up-to-date with commit hook (#2427)

* Update options table in README and keep up-to-date with commit hook

* Separate multiple options with a newline

* remove the old javascript version
---
 .pre-commit-config.yaml            |  6 ++
 README.md                          | 63 +++++++++++--------
 bin/update_readme_options_table.py | 98 ++++++++++++++++++++++++++++++
 docs/options.md                    | 34 -----------
 4 files changed, 140 insertions(+), 61 deletions(-)
 create mode 100755 bin/update_readme_options_table.py

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 22315221d..e8443a7f3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -70,6 +70,12 @@ repos:
     language: python
     entry: bin/update_readme_changelog.py
     files: ^docs/changelog.md$
+  - id: update-readme-options-table
+    name: Update README options table
+    language: python
+    entry: bin/update_readme_options_table.py --force
+    files: ^docs/options.md$
+    pass_filenames: false
 
 - repo: https://github.com/codespell-project/codespell
   rev: v2.4.1
diff --git a/README.md b/README.md
index 574a422e2..d40d7af0a 100644
--- a/README.md
+++ b/README.md
@@ -124,35 +124,44 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
 
 <sup>Explore an interactive version of this diagram [in the docs](https://cibuildwheel.pypa.io/en/stable/#how-it-works).</sup>
 
-Options
--------
+<!-- START bin/update_readme_options_table.py -->
+<!-- This table is auto-generated from docs/options.md by bin/update_readme_options_table.py -->
 
 |   | Option | Description |
-|---|--------|-------------|
-| **Build selection** | [`CIBW_PLATFORM`](https://cibuildwheel.pypa.io/en/stable/options/#platform)  | Override the auto-detected target platform |
-|   | [`CIBW_BUILD`](https://cibuildwheel.pypa.io/en/stable/options/#build-skip)  <br> [`CIBW_SKIP`](https://cibuildwheel.pypa.io/en/stable/options/#build-skip)  | Choose the Python versions to build |
-|   | [`CIBW_ARCHS`](https://cibuildwheel.pypa.io/en/stable/options/#archs)  | Change the architectures built on your machine by default. |
-|   | [`CIBW_PROJECT_REQUIRES_PYTHON`](https://cibuildwheel.pypa.io/en/stable/options/#requires-python)  | Manually set the Python compatibility of your project |
-|   | [`CIBW_PRERELEASE_PYTHONS`](https://cibuildwheel.pypa.io/en/stable/options/#prerelease-pythons)  | Enable building with pre-release versions of Python if available |
-| **Build customization** | [`CIBW_BUILD_FRONTEND`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend)  | Set the tool to use to build, either "pip" (default for now) or "build" |
-|   | [`CIBW_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/stable/options/#environment)  | Set environment variables needed during the build |
-|   | [`CIBW_ENVIRONMENT_PASS_LINUX`](https://cibuildwheel.pypa.io/en/stable/options/#environment-pass)  | Set environment variables on the host to pass-through to the container during the build. |
-|   | [`CIBW_BEFORE_ALL`](https://cibuildwheel.pypa.io/en/stable/options/#before-all)  | Execute a shell command on the build system before any wheels are built. |
-|   | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.pypa.io/en/stable/options/#before-build)  | Execute a shell command preparing each wheel's build |
-|   | [`CIBW_XBUILD_TOOLS`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools)  | Binaries on the path that should be included in an isolated cross-build environment. |
-|   | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command)  | Execute a shell command to repair each built wheel |
-|   | [`CIBW_MANYLINUX_*_IMAGE`<br/>`CIBW_MUSLLINUX_*_IMAGE`](https://cibuildwheel.pypa.io/en/stable/options/#linux-image)  | Specify alternative manylinux / musllinux Docker images |
-|   | [`CIBW_CONTAINER_ENGINE`](https://cibuildwheel.pypa.io/en/stable/options/#container-engine)  | Specify which container engine to use when building Linux wheels |
-|   | [`CIBW_DEPENDENCY_VERSIONS`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions)  | Specify how cibuildwheel controls the versions of the tools it uses |
-| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.pypa.io/en/stable/options/#test-command)  | Execute a shell command to test each built wheel |
-|   | [`CIBW_BEFORE_TEST`](https://cibuildwheel.pypa.io/en/stable/options/#before-test)  | Execute a shell command before testing each wheel |
-|   | [`CIBW_TEST_SOURCES`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources)  | Files and folders from the source tree that are copied into an isolated tree before running the tests |
-|   | [`CIBW_TEST_REQUIRES`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires)  | Install Python dependencies before running the tests |
-|   | [`CIBW_TEST_EXTRAS`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras)  | Install your wheel for testing using extras_require |
-|   | [`CIBW_TEST_SKIP`](https://cibuildwheel.pypa.io/en/stable/options/#test-skip)  | Skip running tests on some builds |
-| **Other** | [`CIBW_BUILD_VERBOSITY`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity)  | Increase/decrease the output of pip wheel |
-
-These options can be specified in a pyproject.toml file, as well; see [configuration](https://cibuildwheel.pypa.io/en/stable/options/#configuration).
+|---|---|---|
+| **Build selection** | [`platform`](https://cibuildwheel.pypa.io/en/stable/options/#platform) | Override the auto-detected target platform |
+|  | [`build`<br>`skip`](https://cibuildwheel.pypa.io/en/stable/options/#build-skip) | Choose the Python versions to build |
+|  | [`archs`](https://cibuildwheel.pypa.io/en/stable/options/#archs) | Change the architectures built on your machine by default. |
+|  | [`project-requires-python`](https://cibuildwheel.pypa.io/en/stable/options/#requires-python) | Manually set the Python compatibility of your project |
+|  | [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) | Enable building with extra categories of selectors present. |
+|  | [`allow-empty`](https://cibuildwheel.pypa.io/en/stable/options/#allow-empty) | Suppress the error code if no wheels match the specified build identifiers |
+| **Build customization** | [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) | Set the tool to use to build, either "build" (default), "build\[uv\]", or "pip" |
+|  | [`config-settings`](https://cibuildwheel.pypa.io/en/stable/options/#config-settings) | Specify config-settings for the build backend. |
+|  | [`environment`](https://cibuildwheel.pypa.io/en/stable/options/#environment) | Set environment variables |
+|  | [`environment-pass`](https://cibuildwheel.pypa.io/en/stable/options/#environment-pass) | Set environment variables on the host to pass-through to the container. |
+|  | [`before-all`](https://cibuildwheel.pypa.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. |
+|  | [`before-build`](https://cibuildwheel.pypa.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
+|  | [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) | Binaries on the path that should be included in an isolated cross-build environment. |
+|  | [`repair-wheel-command`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each built wheel |
+|  | [`manylinux-*-image`<br>`musllinux-*-image`](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) | Specify manylinux / musllinux container images |
+|  | [`container-engine`](https://cibuildwheel.pypa.io/en/stable/options/#container-engine) | Specify the container engine to use when building Linux wheels |
+|  | [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) | Control the versions of the tools cibuildwheel uses |
+|  | [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) | Specify the Pyodide version to use for `pyodide` platform builds |
+| **Testing** | [`test-command`](https://cibuildwheel.pypa.io/en/stable/options/#test-command) | The command to test each built wheel |
+|  | [`before-test`](https://cibuildwheel.pypa.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
+|  | [`test-sources`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources) | Files and folders from the source tree that are copied into an isolated tree before running the tests |
+|  | [`test-requires`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
+|  | [`test-extras`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras) | Install your wheel for testing using `extras_require` |
+|  | [`test-groups`](https://cibuildwheel.pypa.io/en/stable/options/#test-groups) | Specify test dependencies from your project's `dependency-groups` |
+|  | [`test-skip`](https://cibuildwheel.pypa.io/en/stable/options/#test-skip) | Skip running tests on some builds |
+|  | [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) | Set environment variables for the test environment |
+| **Debugging** | [`debug-keep-container`](https://cibuildwheel.pypa.io/en/stable/options/#debug-keep-container) | Keep the container after running for debugging. |
+|  | [`debug-traceback`](https://cibuildwheel.pypa.io/en/stable/options/#debug-traceback) | Print full traceback when errors occur. |
+|  | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build |
+
+<!-- END bin/update_readme_options_table.py -->
+
+These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/).
 
 Working examples
 ----------------
diff --git a/bin/update_readme_options_table.py b/bin/update_readme_options_table.py
new file mode 100755
index 000000000..f78cbeacc
--- /dev/null
+++ b/bin/update_readme_options_table.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+
+import argparse
+import dataclasses
+import re
+from pathlib import Path
+from typing import Final
+
+DIR: Final[Path] = Path(__file__).parent.parent.resolve()
+README: Final[Path] = DIR / "README.md"
+OPTIONS_MD: Final[Path] = DIR / "docs" / "options.md"
+
+SECTION_HEADER_REGEX = re.compile(r"^## (?P<name>.*?)$", re.MULTILINE)
+
+# https://regexr.com/8f1ff
+OPTION_HEADER_REGEX = re.compile(
+    r"^### (?P<name>.*?){.*#(?P<id>\S+).*}\n+> ?(?P<desc>.*)$", re.MULTILINE
+)
+
+README_OPTIONS_TABLE_SECTION = re.compile(
+    r"""(?<=<!-- START bin\/update_readme_options_table.py -->\n).*(?=<!-- END bin\/update_readme_options_table.py -->)""",
+    re.DOTALL,
+)
+
+
+@dataclasses.dataclass(kw_only=True)
+class Option:
+    name: str
+    id: str
+    desc: str
+    section: str
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser(
+        description="Update the options table in the README from docs/options.md"
+    )
+    parser.add_argument(
+        "--force",
+        action="store_true",
+        help="Updates the README inplace, rather than printing to stdout.",
+    )
+    args = parser.parse_args()
+
+    options_md = OPTIONS_MD.read_text(encoding="utf-8")
+
+    sections = SECTION_HEADER_REGEX.split(options_md)[1:]
+
+    options = []
+
+    for section_name, section_content in zip(sections[0::2], sections[1::2], strict=True):
+        for match in OPTION_HEADER_REGEX.finditer(section_content):
+            option = Option(
+                name=match.group("name").strip(),
+                id=match.group("id").strip(),
+                desc=match.group("desc").strip(),
+                section=section_name.strip(),
+            )
+            options.append(option)
+
+    table_md = "<!-- This table is auto-generated from docs/options.md by bin/update_readme_options_table.py -->\n\n"
+    table_md += "|   | Option | Description |\n"
+    table_md += "|---|---|---|\n"
+    last_section: str | None = None
+
+    for option in options:
+        cells: list[str] = []
+
+        cells.append(f"**{option.section}**" if option.section != last_section else "")
+        last_section = option.section
+
+        url = f"https://cibuildwheel.pypa.io/en/stable/options/#{option.id}"
+        name = option.name.replace(", ", "<br>")  # Replace commas with line breaks
+        cells.append(f"[{name}]({url})")
+
+        cells.append(option.desc)
+
+        table_md += "| " + " | ".join(cells) + " |\n"
+    table_md += "\n"
+
+    if not args.force:
+        print(table_md)
+        return
+
+    readme_text = README.read_text(encoding="utf-8")
+
+    if not re.search(README_OPTIONS_TABLE_SECTION, readme_text):
+        msg = "Options section not found in README"
+        raise ValueError(msg)
+
+    readme_text = re.sub(README_OPTIONS_TABLE_SECTION, table_md, readme_text)
+    README.write_text(readme_text, encoding="utf-8")
+
+    print("Updated README with options table.")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/docs/options.md b/docs/options.md
index 95a82e3db..4d7623353 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1889,40 +1889,6 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
       }
     }
 
-    // write the markdown table for the README
-
-    var markdown = ''
-
-    markdown += '|   | Option | Description |\n'
-    markdown += '|---|--------|-------------|\n'
-
-    var prevHeader = null
-
-    for (var i = 0; i < headers.length; i += 1) {
-      var header = headers[i];
-      var headerOptions = options[header];
-      for (var j = 0; j < headerOptions.length; j += 1) {
-        var option = headerOptions[j];
-
-        if (j == 0) {
-          markdown += '| **'+header+'** '
-        } else {
-          markdown += '|   '
-        }
-
-        var optionNames = option.name.trim().split(', ')
-        var url = 'https://cibuildwheel.pypa.io/en/stable/options/#'+option.id;
-        var namesMarkdown = $.map(optionNames, function(n) {
-          return '[`'+n+'`]('+url+') '
-        }).join(' <br> ')
-
-        markdown += '| '+namesMarkdown+' '
-        markdown += '| '+option.description.trim()+' '
-        markdown += '|\n'
-      }
-    }
-    console.log('readme options markdown\n', markdown)
-
     // add the option tags to each heading
     $('.rst-content h3')
       .each(function (i, el) {

From 0e8f4f777d6bfb555a00ae0d2f679fd9d0611681 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Wed, 28 May 2025 19:42:00 -0400
Subject: [PATCH 094/193] [Bot] Update dependencies (#2426)

* Update dependencies

* tests: ignore tag order

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: use sorted platform tags

Per PEP 425, compressed tag sets should be sorted.
This was fixed in auditwheel 6.4.0

* tests: use sorted platform tags

this shouldn't change that often, use static order to allow override.

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   | 24 ++++-----
 .../resources/constraints-pyodide312.txt      |  2 +-
 .../resources/constraints-python310.txt       |  2 +-
 .../resources/constraints-python39.txt        |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 test/test_0_basic.py                          |  5 +-
 test/test_manylinuxXXXX_only.py               |  2 +-
 test/utils.py                                 |  8 +--
 8 files changed, 51 insertions(+), 48 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 10af09281..00c555794 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -165,12 +165,12 @@ python_configurations = [
   { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
   { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
   { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
-  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
-  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
-  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
-  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
-  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
-  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b1-macos11.pkg" },
+  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
+  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
+  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
+  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
+  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
+  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
   { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
   { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
   { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
@@ -199,18 +199,18 @@ python_configurations = [
   { identifier = "cp313t-win32", version = "3.13.3", arch = "32" },
   { identifier = "cp313-win_amd64", version = "3.13.3", arch = "64" },
   { identifier = "cp313t-win_amd64", version = "3.13.3", arch = "64" },
-  { identifier = "cp314-win32", version = "3.14.0-b1", arch = "32" },
-  { identifier = "cp314t-win32", version = "3.14.0-b1", arch = "32" },
-  { identifier = "cp314-win_amd64", version = "3.14.0-b1", arch = "64" },
-  { identifier = "cp314t-win_amd64", version = "3.14.0-b1", arch = "64" },
+  { identifier = "cp314-win32", version = "3.14.0-b2", arch = "32" },
+  { identifier = "cp314t-win32", version = "3.14.0-b2", arch = "32" },
+  { identifier = "cp314-win_amd64", version = "3.14.0-b2", arch = "64" },
+  { identifier = "cp314t-win_amd64", version = "3.14.0-b2", arch = "64" },
   { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
   { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
   { identifier = "cp313-win_arm64", version = "3.13.3", arch = "ARM64" },
   { identifier = "cp313t-win_arm64", version = "3.13.3", arch = "ARM64" },
-  { identifier = "cp314-win_arm64", version = "3.14.0-b1", arch = "ARM64" },
-  { identifier = "cp314t-win_arm64", version = "3.14.0-b1", arch = "ARM64" },
+  { identifier = "cp314-win_arm64", version = "3.14.0-b2", arch = "ARM64" },
+  { identifier = "cp314t-win_arm64", version = "3.14.0-b2", arch = "ARM64" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 5586115d0..8f4a6e5c7 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -89,7 +89,7 @@ shellingham==1.5.4
     # via typer
 sniffio==1.3.1
     # via anyio
-typer==0.15.4
+typer==0.16.0
     # via
     #   auditwheel-emscripten
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 5883ddfe0..0a4aff319 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -30,5 +30,5 @@ typing-extensions==4.13.2
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
-zipp==3.21.0
+zipp==3.22.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 5883ddfe0..0a4aff319 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -30,5 +30,5 @@ typing-extensions==4.13.2
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
-zipp==3.21.0
+zipp==3.22.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index b137183d2..83a71dce2 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.24-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.24-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.24-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.28-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.24-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.28-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.24-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.24-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.24-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.28-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.24-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.24-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.24-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.28-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.24-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.24-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.24-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.28-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.24-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.24-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.28-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.28-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.24-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.24-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.24-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.28-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.24-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.24-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.28-1
 
 [riscv64]
 
diff --git a/test/test_0_basic.py b/test/test_0_basic.py
index c1a8dfcbf..2524d6286 100644
--- a/test/test_0_basic.py
+++ b/test/test_0_basic.py
@@ -1,5 +1,6 @@
 import textwrap
 
+import packaging.utils
 import pytest
 
 from cibuildwheel.logger import Logger
@@ -37,7 +38,9 @@ def test(tmp_path, build_frontend_env, capfd):
 
     # check that the expected wheels are produced
     expected_wheels = utils.expected_wheels("spam", "0.1.0")
-    assert set(actual_wheels) == set(expected_wheels)
+    actual_wheels_normalized = {packaging.utils.parse_wheel_filename(w) for w in actual_wheels}
+    expected_wheels_normalized = {packaging.utils.parse_wheel_filename(w) for w in expected_wheels}
+    assert actual_wheels_normalized == expected_wheels_normalized
 
     enable_groups = utils.get_enable_groups()
     if EnableGroup.GraalPy not in enable_groups:
diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py
index 5f0839711..483d40f99 100644
--- a/test/test_manylinuxXXXX_only.py
+++ b/test/test_manylinuxXXXX_only.py
@@ -95,7 +95,7 @@ def test(manylinux_image, tmp_path):
     actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
 
     platform_tag_map = {
-        "manylinux2014": ["manylinux_2_17", "manylinux2014"],
+        "manylinux2014": ["manylinux2014", "manylinux_2_17"],
     }
     expected_wheels = utils.expected_wheels(
         "spam",
diff --git a/test/utils.py b/test/utils.py
index 55b492a81..838e2179c 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -250,11 +250,11 @@ def _expected_wheels(
 
     if manylinux_versions is None:
         manylinux_versions = {
-            "armv7l": ["manylinux_2_17", "manylinux2014", "manylinux_2_31"],
-            "i686": ["manylinux_2_5", "manylinux1", "manylinux_2_17", "manylinux2014"],
-            "x86_64": ["manylinux_2_5", "manylinux1", "manylinux_2_28"],
+            "armv7l": ["manylinux2014", "manylinux_2_17", "manylinux_2_31"],
+            "i686": ["manylinux1", "manylinux2014", "manylinux_2_17", "manylinux_2_5"],
+            "x86_64": ["manylinux1", "manylinux_2_28", "manylinux_2_5"],
             "riscv64": ["manylinux_2_31", "manylinux_2_35"],
-        }.get(machine_arch, ["manylinux_2_17", "manylinux2014", "manylinux_2_28"])
+        }.get(machine_arch, ["manylinux2014", "manylinux_2_17", "manylinux_2_28"])
 
     if musllinux_versions is None:
         musllinux_versions = ["musllinux_1_2"]

From 2d6c328457b11668770e3074919da8689c5afa7d Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 28 May 2025 19:43:19 -0400
Subject: [PATCH 095/193] fix: drop PYTHONSAFEPATH (#2429)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/platforms/linux.py   |  1 -
 cibuildwheel/platforms/macos.py   |  3 ---
 cibuildwheel/platforms/pyodide.py |  2 --
 cibuildwheel/platforms/windows.py |  3 ---
 docs/options.md                   | 14 ++++----------
 test/test_testing.py              | 10 ++--------
 6 files changed, 6 insertions(+), 27 deletions(-)

diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 31f02cc85..00cd85b2f 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -361,7 +361,6 @@ def build_in_container(
             virtualenv_env = env.copy()
             virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
             virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
-            virtualenv_env["PYTHONSAFEPATH"] = "1"
             virtualenv_env = build_options.test_environment.as_dictionary(
                 prev_environment=virtualenv_env
             )
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 9dae02e5f..3b4534d2a 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -648,9 +648,6 @@ def build(options: Options, tmp_path: Path) -> None:
 
                     virtualenv_env["MACOSX_DEPLOYMENT_TARGET"] = get_test_macosx_deployment_target()
 
-                    # see https://github.com/pypa/cibuildwheel/issues/2358 for discussion
-                    virtualenv_env["PYTHONSAFEPATH"] = "1"
-
                     virtualenv_env = build_options.test_environment.as_dictionary(
                         prev_environment=virtualenv_env
                     )
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index fdabc3aa0..920920923 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -484,8 +484,6 @@ def build(options: Options, tmp_path: Path) -> None:
                 )
                 virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
 
-                virtualenv_env["PYTHONSAFEPATH"] = "1"
-
                 virtualenv_env = build_options.test_environment.as_dictionary(
                     prev_environment=virtualenv_env
                 )
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 17375fad4..cce744d5f 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -553,9 +553,6 @@ def build(options: Options, tmp_path: Path) -> None:
                     pip_version=pip_version,
                 )
 
-                # see https://github.com/pypa/cibuildwheel/issues/2358 for discussion
-                virtualenv_env["PYTHONSAFEPATH"] = "1"
-
                 virtualenv_env = build_options.test_environment.as_dictionary(
                     prev_environment=virtualenv_env
                 )
diff --git a/docs/options.md b/docs/options.md
index 4d7623353..fcebb386e 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1604,12 +1604,6 @@ A space-separated list of environment variables to set in the test environment.
 
 The syntax is the same as for [`environment`](#environment).
 
-cibuildwheel sets the variable
-[`PYTHONSAFEPATH`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSAFEPATH)
-to avoid picking up in-tree dependencies when running the tests - we want to
-test the installed wheel, not the in-tree version. However, this can be
-overridden by setting `PYTHONSAFEPATH` to an empty string.
-
 Platform-specific environment variables are also available:<br/>
 `CIBW_TEST_ENVIRONMENT_MACOS` | `CIBW_TEST_ENVIRONMENT_WINDOWS` | `CIBW_TEST_ENVIRONMENT_LINUX` | `CIBW_TEST_ENVIRONMENT_IOS` | `CIBW_TEST_ENVIRONMENT_PYODIDE`
 
@@ -1622,8 +1616,8 @@ Platform-specific environment variables are also available:<br/>
     # Set the environment variable MY_ENV_VAR to "my_value" in the test environment
     test-environment = { MY_ENV_VAR="my_value" }
 
-    # Unset PYTHONSAFEPATH in the test environment
-    test-environment = { PYTHONSAFEPATH="" }
+    # Set PYTHONSAFEPATH in the test environment
+    test-environment = { PYTHONSAFEPATH="1" }
     ```
 
 !!! tab examples "Environment variables"
@@ -1632,8 +1626,8 @@ Platform-specific environment variables are also available:<br/>
     # Set the environment variable MY_ENV_VAR to "my_value" in the test environment
     CIBW_TEST_ENVIRONMENT: MY_ENV_VAR=my_value
 
-    # Unset PYTHONSAFEPATH in the test environment
-    CIBW_TEST_ENVIRONMENT: PYTHONSAFEPATH=
+    # Set PYTHONSAFEPATH in the test environment
+    CIBW_TEST_ENVIRONMENT: PYTHONSAFEPATH=1
     ```
 
 
diff --git a/test/test_testing.py b/test/test_testing.py
index f12e7b5d5..92de0e87e 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -66,12 +66,6 @@ def test_uname(self):
         bits = struct.calcsize("P") * 8
         if bits == 32:
             self.assertIn(platform.machine(), ["i686", "armv7l","armv8l", "wasm32"])
-
-    def test_pythonsafepath(self):
-        # check that the PYTHONSAFEPATH environment variable is set
-        value = os.environ.get("PYTHONSAFEPATH")
-        self.assertIsNotNone(value)
-        self.assertNotEqual(value, "")
 '''
 
 
@@ -256,8 +250,8 @@ def test_test_environment(tmp_path):
     actual_wheels = utils.cibuildwheel_run(
         project_dir,
         add_env={
-            "CIBW_TEST_ENVIRONMENT": "MYVAR=somevalue PYTHONSAFEPATH=",
-            "CIBW_TEST_COMMAND": "python -c \"import os; assert os.environ.get('MYVAR') == 'somevalue'; assert os.environ.get('PYTHONSAFEPATH') == ''\"",
+            "CIBW_TEST_ENVIRONMENT": "MYVAR=somevalue PYTHONSAFEPATH=1",
+            "CIBW_TEST_COMMAND": "python -c \"import os; assert os.environ.get('MYVAR') == 'somevalue'; assert os.environ.get('PYTHONSAFEPATH') == '1'\"",
         },
     )
     # also check that we got the right wheels

From b9f1942e8d3957b623780e4a621a23a03364557b Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 28 May 2025 23:36:16 -0400
Subject: [PATCH 096/193] chore: use cog for quick README updates (#2428)

* chore: use cog for quick README updates

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* refactor: rename scripts

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .pre-commit-config.yaml                       | 17 +++----
 README.md                                     | 15 +++---
 bin/readme_changelog.py                       | 23 +++++++++
 ...tions_table.py => readme_options_table.py} | 38 ++-------------
 bin/update_readme_changelog.py                | 48 -------------------
 5 files changed, 41 insertions(+), 100 deletions(-)
 create mode 100755 bin/readme_changelog.py
 rename bin/{update_readme_options_table.py => readme_options_table.py} (60%)
 delete mode 100755 bin/update_readme_changelog.py

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e8443a7f3..7831eae69 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -62,20 +62,15 @@ repos:
     name: Disallow improper capitalization
     language: pygrep
     entry: PyBind|Numpy|Cmake|Github|PyTest
-    types:
-    - markdown
+    types: [markdown]
     exclude: ^docs/working-examples\.md$  # Autogenerated
-  - id: update-readme-changelog
-    name: Update README changelog
+  - id: cog
+    name: Cog the README
     language: python
-    entry: bin/update_readme_changelog.py
-    files: ^docs/changelog.md$
-  - id: update-readme-options-table
-    name: Update README options table
-    language: python
-    entry: bin/update_readme_options_table.py --force
-    files: ^docs/options.md$
     pass_filenames: false
+    entry: cog -c -P -r -I ./bin README.md
+    files: '^(README\.md|docs/changelog\.md|docs/options\.md|bin/readme.*)$'
+    additional_dependencies: [cogapp]
 
 - repo: https://github.com/codespell-project/codespell
   rev: v2.4.1
diff --git a/README.md b/README.md
index d40d7af0a..8a8f9c544 100644
--- a/README.md
+++ b/README.md
@@ -124,8 +124,10 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
 
 <sup>Explore an interactive version of this diagram [in the docs](https://cibuildwheel.pypa.io/en/stable/#how-it-works).</sup>
 
-<!-- START bin/update_readme_options_table.py -->
-<!-- This table is auto-generated from docs/options.md by bin/update_readme_options_table.py -->
+
+<!--[[[cog from readme_options_table import get_table; print(get_table()) ]]]-->
+
+<!-- This table is auto-generated from docs/options.md by bin/readme_options_table.py -->
 
 |   | Option | Description |
 |---|---|---|
@@ -159,7 +161,8 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
 |  | [`debug-traceback`](https://cibuildwheel.pypa.io/en/stable/options/#debug-traceback) | Print full traceback when errors occur. |
 |  | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build |
 
-<!-- END bin/update_readme_options_table.py -->
+
+<!--[[[end]]] (checksum: 4d6a8418630e9ed43251973d93798a1b) -->
 
 These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/).
 
@@ -222,9 +225,7 @@ This is similar to static linking, so it might have some license implications. C
 Changelog
 =========
 
-<!-- START bin/update_readme_changelog.py -->
-
-<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
+<!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
 
 ### v3.0.0
 
@@ -278,7 +279,7 @@ _26 April 2025_
 
 - 🛠 Dependency updates, including Python 3.13.3 (#2371)
 
-<!-- END bin/update_readme_changelog.py -->
+<!-- [[[end]]] (checksum: ccccc1a3fec30c19a762ec4575fbd18d) -->
 
 ---
 
diff --git a/bin/readme_changelog.py b/bin/readme_changelog.py
new file mode 100755
index 000000000..29c30293e
--- /dev/null
+++ b/bin/readme_changelog.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+
+import re
+from pathlib import Path
+
+PROJECT_ROOT = Path(__file__).parent / ".."
+CHANGELOG_FILE = PROJECT_ROOT / "docs" / "changelog.md"
+
+# https://regexr.com/622ds
+FIRST_5_CHANGELOG_ENTRIES_REGEX = re.compile(r"""(^###.*?(?=###)){5}""", re.DOTALL | re.MULTILINE)
+
+
+def mini_changelog() -> str:
+    changelog_text = CHANGELOG_FILE.read_text()
+
+    mini_changelog_match = FIRST_5_CHANGELOG_ENTRIES_REGEX.search(changelog_text)
+    assert mini_changelog_match, "Failed to find the first few changelog entries"
+
+    return f"\n{mini_changelog_match.group(0).strip()}\n"
+
+
+if __name__ == "__main__":
+    print(mini_changelog())
diff --git a/bin/update_readme_options_table.py b/bin/readme_options_table.py
similarity index 60%
rename from bin/update_readme_options_table.py
rename to bin/readme_options_table.py
index f78cbeacc..203d6bf5d 100755
--- a/bin/update_readme_options_table.py
+++ b/bin/readme_options_table.py
@@ -1,13 +1,11 @@
 #!/usr/bin/env python3
 
-import argparse
 import dataclasses
 import re
 from pathlib import Path
 from typing import Final
 
 DIR: Final[Path] = Path(__file__).parent.parent.resolve()
-README: Final[Path] = DIR / "README.md"
 OPTIONS_MD: Final[Path] = DIR / "docs" / "options.md"
 
 SECTION_HEADER_REGEX = re.compile(r"^## (?P<name>.*?)$", re.MULTILINE)
@@ -17,11 +15,6 @@
     r"^### (?P<name>.*?){.*#(?P<id>\S+).*}\n+> ?(?P<desc>.*)$", re.MULTILINE
 )
 
-README_OPTIONS_TABLE_SECTION = re.compile(
-    r"""(?<=<!-- START bin\/update_readme_options_table.py -->\n).*(?=<!-- END bin\/update_readme_options_table.py -->)""",
-    re.DOTALL,
-)
-
 
 @dataclasses.dataclass(kw_only=True)
 class Option:
@@ -31,17 +24,7 @@ class Option:
     section: str
 
 
-def main() -> None:
-    parser = argparse.ArgumentParser(
-        description="Update the options table in the README from docs/options.md"
-    )
-    parser.add_argument(
-        "--force",
-        action="store_true",
-        help="Updates the README inplace, rather than printing to stdout.",
-    )
-    args = parser.parse_args()
-
+def get_table() -> str:
     options_md = OPTIONS_MD.read_text(encoding="utf-8")
 
     sections = SECTION_HEADER_REGEX.split(options_md)[1:]
@@ -58,7 +41,7 @@ def main() -> None:
             )
             options.append(option)
 
-    table_md = "<!-- This table is auto-generated from docs/options.md by bin/update_readme_options_table.py -->\n\n"
+    table_md = "\n<!-- This table is auto-generated from docs/options.md by bin/readme_options_table.py -->\n\n"
     table_md += "|   | Option | Description |\n"
     table_md += "|---|---|---|\n"
     last_section: str | None = None
@@ -78,21 +61,8 @@ def main() -> None:
         table_md += "| " + " | ".join(cells) + " |\n"
     table_md += "\n"
 
-    if not args.force:
-        print(table_md)
-        return
-
-    readme_text = README.read_text(encoding="utf-8")
-
-    if not re.search(README_OPTIONS_TABLE_SECTION, readme_text):
-        msg = "Options section not found in README"
-        raise ValueError(msg)
-
-    readme_text = re.sub(README_OPTIONS_TABLE_SECTION, table_md, readme_text)
-    README.write_text(readme_text, encoding="utf-8")
-
-    print("Updated README with options table.")
+    return table_md
 
 
 if __name__ == "__main__":
-    main()
+    print(get_table())
diff --git a/bin/update_readme_changelog.py b/bin/update_readme_changelog.py
deleted file mode 100755
index 024a23373..000000000
--- a/bin/update_readme_changelog.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env python3
-
-
-import re
-import sys
-from pathlib import Path
-
-PROJECT_ROOT = Path(__file__).parent / ".."
-CHANGELOG_FILE = PROJECT_ROOT / "docs" / "changelog.md"
-README_FILE = PROJECT_ROOT / "README.md"
-
-# https://regexr.com/622ds
-FIRST_5_CHANGELOG_ENTRIES_REGEX = re.compile(r"""(^###.*?(?=###)){5}""", re.DOTALL | re.MULTILINE)
-
-# https://regexr.com/622e5
-README_CHANGELOG_SECTION = re.compile(
-    r"""(?<=<!-- START bin\/update_readme_changelog.py -->\n).*(?=<!-- END bin\/update_readme_changelog.py -->)""",
-    re.DOTALL,
-)
-
-
-def main() -> None:
-    changelog_text = CHANGELOG_FILE.read_text()
-    readme_text = README_FILE.read_text()
-
-    mini_changelog_match = FIRST_5_CHANGELOG_ENTRIES_REGEX.search(changelog_text)
-    assert mini_changelog_match, "Failed to find the first few changelog entries"
-
-    mini_changelog = "\n".join(
-        [
-            "",
-            "<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->",
-            "",
-            mini_changelog_match.group(0).strip(),
-            "",
-            "",
-        ]
-    )
-
-    if not re.search(README_CHANGELOG_SECTION, readme_text):
-        sys.exit("Changelog section not found in README")
-
-    readme_text = re.sub(README_CHANGELOG_SECTION, mini_changelog, readme_text)
-    README_FILE.write_text(readme_text)
-
-
-if __name__ == "__main__":
-    main()

From cf078b0954f3fd08b8445a7bf2c3fb83ab3bb971 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Thu, 29 May 2025 00:27:20 -0400
Subject: [PATCH 097/193] Bump version: v3.0.0b4

---
 README.md                              | 18 ++++++++++--------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  8 ++++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/README.md b/README.md
index 8a8f9c544..ecce87666 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0b3
+        run: python -m pip install cibuildwheel==3.0.0b4
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -239,6 +239,14 @@ If you've used previous versions of the beta:
 
 #### v3.0.0b3
 
+_29 May 2025_
+
+- 🛠 Dependency updates, including Python 3.14.0b2 (#2371)
+- 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
+- 📚 README table now matches docs and auto-updates. (#2427, #2428)
+
+#### v3.0.0b3
+
 _28 May 2025_
 
 - 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
@@ -273,13 +281,7 @@ _19 May 2025_
 - ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
 - 📚 A reorganisation of the docs, and numerous updates (#2280)
 
-### v2.23.3
-
-_26 April 2025_
-
-- 🛠 Dependency updates, including Python 3.13.3 (#2371)
-
-<!-- [[[end]]] (checksum: ccccc1a3fec30c19a762ec4575fbd18d) -->
+<!-- [[[end]]] (checksum: 1e79ae989c2c03ebaf8fa9fc77de7612) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 6ab976e35..e5b84fbcd 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0b3"
+__version__ = "3.0.0b4"
diff --git a/docs/changelog.md b/docs/changelog.md
index fce8d721a..7341793ca 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -16,6 +16,14 @@ If you've used previous versions of the beta:
 
 #### v3.0.0b3
 
+_29 May 2025_
+
+- 🛠 Dependency updates, including Python 3.14.0b2 (#2371)
+- 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
+- 📚 README table now matches docs and auto-updates. (#2427, #2428)
+
+#### v3.0.0b3
+
 _28 May 2025_
 
 - 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
diff --git a/docs/faq.md b/docs/faq.md
index 9a04e216c..4db883017 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0b3
+uses: pypa/cibuildwheel@v3.0.0b4
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0b3
+cibuildwheel==3.0.0b4
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0b3 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0b4 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index fa805c94f..786925848 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0b3
+        pip3 install cibuildwheel==3.0.0b4
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0b3
+        python3 -m pip install cibuildwheel==3.0.0b4
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0b3
+        pip install cibuildwheel==3.0.0b4
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index aa27628a8..46d7ca97f 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b3
+            python3 -m pip install --user cibuildwheel==3.0.0b4
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b3
+            python3 -m pip install --user cibuildwheel==3.0.0b4
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0b3
+            pip3 install cibuildwheel==3.0.0b4
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 3dd47a778..df5221d1d 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b3
+    - python -m pip install cibuildwheel==3.0.0b4
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 1688a1542..4b476a5c5 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b3
+    - python -m pip install cibuildwheel==3.0.0b4
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 69b9e3037..54d5160de 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b3
+        uses: pypa/cibuildwheel@v3.0.0b4
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 7b08021a4..ddbb1fc26 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b3
+        uses: pypa/cibuildwheel@v3.0.0b4
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index a9643590c..6f8c4e855 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0b3
+        run: pipx run cibuildwheel==3.0.0b4
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 32c6abdd2..e1308d701 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b3
+        uses: pypa/cibuildwheel@v3.0.0b4
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index cdea6e172..030237dda 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0b3
+    - python -m pip install cibuildwheel==3.0.0b4
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0b3
+    - py -m pip install cibuildwheel==3.0.0b4
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0b3
+    - python3 -m pip install cibuildwheel==3.0.0b4
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 320a3788b..48e968046 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0b3
+    - python -m pip install cibuildwheel==3.0.0b4
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index 9210526c5..6d382c7ea 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b3
+  - python3 -m pip install cibuildwheel==3.0.0b4
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 0645a1189..164751168 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b3
+  - python3 -m pip install cibuildwheel==3.0.0b4
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index a0e90f1f8..2498adef5 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0b3 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b4 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0b3 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b4 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 5840e43a7..b52371de8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0b3"
+version = "3.0.0b4"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From e8a7a374eef98a6486cb95d8977f9d2cbd76d869 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 29 May 2025 00:33:43 -0400
Subject: [PATCH 098/193] docs: typo b3 -> b4

---
 docs/changelog.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/changelog.md b/docs/changelog.md
index 7341793ca..7552e9069 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -14,7 +14,7 @@ If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 
-#### v3.0.0b3
+#### v3.0.0b4
 
 _29 May 2025_
 

From 6f2d68febffc08fc79573c57c6d8f0c28baccf5f Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Thu, 29 May 2025 12:24:39 +0100
Subject: [PATCH 099/193] Add missing changelog entries

---
 README.md         | 15 ++++++++++++---
 docs/changelog.md | 15 ++++++++++++---
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index ecce87666..be390f213 100644
--- a/README.md
+++ b/README.md
@@ -233,6 +233,10 @@ Not yet released, but available for testing.
 
 Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
+<!--
+note to self, when doing final release, change to docs URLs in this section to the stable version!
+-->
+
 If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
@@ -274,10 +278,15 @@ _19 May 2025_
     - ~If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).~
     - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory.
 
-- ✨ Added `dependency-versions` inline syntax (#2123)
-- 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
+- ✨ Added´ `dependency-versions` inline syntax (#2123)
+- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/latest/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28` (#2330)
+- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/latest/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
+- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/latest/options/#enable) option instead. (#2095)
+- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish (#2316)
 - 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
-- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`.
+- ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
+- ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
+- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`. (#2095)
 - ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
 - 📚 A reorganisation of the docs, and numerous updates (#2280)
 
diff --git a/docs/changelog.md b/docs/changelog.md
index 7552e9069..d267bd3af 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -10,6 +10,10 @@ Not yet released, but available for testing.
 
 Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
 
+<!--
+note to self, when doing final release, change to docs URLs in this section to the stable version!
+-->
+
 If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
@@ -51,10 +55,15 @@ _19 May 2025_
     - ~If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).~
     - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory.
 
-- ✨ Added `dependency-versions` inline syntax (#2123)
-- 🛠 EOL manylinux options can no longer be specified by their shortname. Full OCI URL can still be used for these images, if you wish (#2316)
+- ✨ Added´ `dependency-versions` inline syntax (#2123)
+- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/latest/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28` (#2330)
+- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/latest/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
+- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/latest/options/#enable) option instead. (#2095)
+- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish (#2316)
 - 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
-- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`.
+- ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
+- ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
+- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`. (#2095)
 - ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
 - 📚 A reorganisation of the docs, and numerous updates (#2280)
 

From c5a3ced8c8150302ceb8aa284acce6ed5a5fe225 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Thu, 29 May 2025 10:37:55 -0400
Subject: [PATCH 100/193] docs: rerender README

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index be390f213..b6e689472 100644
--- a/README.md
+++ b/README.md
@@ -241,7 +241,7 @@ If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 
-#### v3.0.0b3
+#### v3.0.0b4
 
 _29 May 2025_
 
@@ -290,7 +290,7 @@ _19 May 2025_
 - ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
 - 📚 A reorganisation of the docs, and numerous updates (#2280)
 
-<!-- [[[end]]] (checksum: 1e79ae989c2c03ebaf8fa9fc77de7612) -->
+<!-- [[[end]]] (checksum: b231dd6d65d5003b403f91d925140069) -->
 
 ---
 

From 733573feb32f2626f99bcf3fd62bd23bf776923f Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Thu, 29 May 2025 15:52:46 +0100
Subject: [PATCH 101/193] Debug Cirrus CI testing (#2414)

* Skip Cirrus CI iOS testing

* Remove support tick in readme

* Debugging cirrus windows failure

* Use a separate file to avoid error messages from vsdevcmd getting into the output

* cirrus: TEMP target just the test i care about

* Revert "cirrus: TEMP target just the test i care about"

This reverts commit 7ed4417970930ab9e0e1cb1f17497d406310d6ff.

* Remove debug code

* Fix windows issues with multiline -c Python commands
---
 README.md                         |  2 +-
 cibuildwheel/platforms/windows.py | 53 +++++++++++++++--------------
 test/test_ios.py                  | 55 +++++++++++++------------------
 3 files changed, 50 insertions(+), 60 deletions(-)

diff --git a/README.md b/README.md
index b6e689472..151820ea1 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ Usage
 | Travis CI       | ✅    |       | ✅      | ✅        |           |             |     |
 | CircleCI        | ✅    | ✅    |         | ✅        | ✅        |             | ✅³  |
 | Gitlab CI       | ✅    | ✅    | ✅      | ✅¹       | ✅        |             | ✅³  |
-| Cirrus CI       | ✅    | ✅    | ✅      | ✅        | ✅        |             | ✅³  |
+| Cirrus CI       | ✅    | ✅    | ✅      | ✅        | ✅        |             |      |
 
 <sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#emulation), distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.</sup><br>
 <sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index cce744d5f..521695b60 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -336,37 +336,36 @@ def setup_python(
         # variables. Adapted from
         # https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt
         # Remove when https://github.com/oracle/graalpython/issues/492 is fixed.
-        vcpath = subprocess.check_output(
-            [
-                Path(os.environ["PROGRAMFILES(X86)"])
-                / "Microsoft Visual Studio"
-                / "Installer"
-                / "vswhere.exe",
-                "-products",
-                "*",
-                "-latest",
-                "-property",
-                "installationPath",
-            ],
-            text=True,
+        vcpath = call(
+            Path(os.environ["PROGRAMFILES(X86)"])
+            / "Microsoft Visual Studio"
+            / "Installer"
+            / "vswhere.exe",
+            "-products",
+            "*",
+            "-latest",
+            "-property",
+            "installationPath",
+            capture_stdout=True,
         ).strip()
         log.notice(f"Discovering Visual Studio for GraalPy at {vcpath}")
-        vcvars = subprocess.check_output(
-            [
-                f"{vcpath}\\Common7\\Tools\\vsdevcmd.bat",
-                "-no_logo",
-                "-arch=amd64",
-                "-host_arch=amd64",
-                "&&",
-                "python",
-                "-c",
-                "import os, json, sys; json.dump(dict(os.environ), sys.stdout);",
-            ],
-            shell=True,
-            text=True,
+        vcvars_file = tmp / "vcvars.json"
+        call(
+            f"{vcpath}\\Common7\\Tools\\vsdevcmd.bat",
+            "-no_logo",
+            "-arch=amd64",
+            "-host_arch=amd64",
+            "&&",
+            "python",
+            "-c",
+            # this command needs to be one line for Windows reasons
+            "import sys, json, pathlib, os; pathlib.Path(sys.argv[1]).write_text(json.dumps(dict(os.environ)))",
+            vcvars_file,
             env=env,
         )
-        env.update(json.loads(vcvars))
+        with open(vcvars_file, encoding="utf-8") as f:
+            vcvars = json.load(f)
+        env.update(vcvars)
 
     return base_python, env
 
diff --git a/test/test_ios.py b/test/test_ios.py
index 9ee588b26..3d1674585 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -8,6 +8,8 @@
 
 import pytest
 
+from cibuildwheel.ci import CIProvider, detect_ci_provider
+
 from . import test_projects, utils
 
 basic_project_files = {
@@ -23,6 +25,19 @@ def test_platform(self):
 }
 
 
+def skip_if_ios_testing_not_supported() -> None:
+    """Skip the test if iOS testing is not supported on this machine."""
+    if utils.get_platform() != "macos":
+        pytest.skip("this test can only run on macOS")
+    if utils.get_xcode_version() < (13, 0):
+        pytest.skip("this test only works with Xcode 13.0 or greater")
+    if detect_ci_provider() == CIProvider.cirrus_ci:
+        pytest.skip(
+            "iOS testing not currently supported on Cirrus CI due to a failure "
+            "to start the simulator."
+        )
+
+
 # iOS tests shouldn't be run in parallel, because they're dependent on calling
 # Xcode, and starting a simulator. These are both multi-threaded operations, and
 # it's easy to overload the CI machine if there are multiple test processes
@@ -39,10 +54,7 @@ def test_platform(self):
     ],
 )
 def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     # Create a temporary "bin" directory, symlink a tool that we know eixsts
     # (/usr/bin/true) into that location under a name that should be unique,
@@ -94,10 +106,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
 @pytest.mark.serial
 def test_no_test_sources(tmp_path, capfd):
     """Build will provide a helpful error if pytest is run and test-sources is not defined."""
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
     basic_project = test_projects.new_c_project()
@@ -126,10 +135,7 @@ def test_no_test_sources(tmp_path, capfd):
 
 def test_ios_testing_with_placeholder(tmp_path, capfd):
     """Build will run tests with the {project} placeholder."""
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
     basic_project = test_projects.new_c_project()
@@ -155,10 +161,7 @@ def test_ios_testing_with_placeholder(tmp_path, capfd):
 
 def test_missing_xbuild_tool(tmp_path, capfd):
     """Build will fail if xbuild-tools references a non-existent tool."""
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
     basic_project = test_projects.new_c_project()
@@ -183,10 +186,7 @@ def test_missing_xbuild_tool(tmp_path, capfd):
 
 def test_no_xbuild_tool_definition(tmp_path, capfd):
     """Build will succeed with a warning if there is no xbuild-tools definition."""
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
     basic_project = test_projects.new_c_project()
@@ -220,10 +220,7 @@ def test_no_xbuild_tool_definition(tmp_path, capfd):
 
 def test_empty_xbuild_tool_definition(tmp_path, capfd):
     """Build will succeed with no warning if there is an empty xbuild-tools definition."""
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
     basic_project = test_projects.new_c_project()
@@ -255,10 +252,7 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd):
 @pytest.mark.serial
 def test_ios_test_command_without_python_dash_m(tmp_path, capfd):
     """pytest should be able to run without python -m, but it should warn."""
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
 
@@ -296,10 +290,7 @@ def test_spam():
 
 def test_ios_test_command_invalid(tmp_path, capfd):
     """Test command should raise an error if it's clearly invalid."""
-    if utils.get_platform() != "macos":
-        pytest.skip("this test can only run on macOS")
-    if utils.get_xcode_version() < (13, 0):
-        pytest.skip("this test only works with Xcode 13.0 or greater")
+    skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
     basic_project = test_projects.new_c_project()

From 3431adedba905aa473dcb104f124232e2bef741e Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 29 May 2025 19:44:38 -0400
Subject: [PATCH 102/193] fix: use standard schema line (#2433)

This was a workaround for a bug in validate-pyproject, but that bug was fixed some time ago, so we can use the standard line here now.

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 bin/generate_schema.py                          | 3 +--
 cibuildwheel/resources/cibuildwheel.schema.json | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index 2c51597fd..6b98ce8ef 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -21,7 +21,7 @@
 args = parser.parse_args()
 
 starter = """
-$schema: http://json-schema.org/draft-07/schema
+$schema: http://json-schema.org/draft-07/schema#
 $id: https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/resources/cibuildwheel.schema.json
 $defs:
   inherit:
@@ -365,7 +365,6 @@ def as_object(d: dict[str, Any]) -> dict[str, Any]:
 
 if args.schemastore:
     schema["$id"] = "https://json.schemastore.org/partial-cibuildwheel.json"
-    schema["$schema"] = "http://json-schema.org/draft-07/schema#"
     schema["description"] = (
         "cibuildwheel's toml file, generated with ./bin/generate_schema.py --schemastore from cibuildwheel."
     )
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index a66b0aa77..92113e3d7 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json-schema.org/draft-07/schema",
+  "$schema": "http://json-schema.org/draft-07/schema#",
   "$id": "https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/resources/cibuildwheel.schema.json",
   "$defs": {
     "inherit": {

From f8168e2a892fe83dfcfea5f00d2516de61ff0ca3 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 29 May 2025 19:54:00 -0400
Subject: [PATCH 103/193] docs: found more to convert to toml (#2430)

* docs: found more to convert to toml

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
---
 README.md            |  2 +-
 docs/contributing.md |  2 +-
 docs/faq.md          | 18 +++++++++---------
 docs/platforms.md    | 24 ++++++++++++------------
 4 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/README.md b/README.md
index 151820ea1..2b2587cc3 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@ While cibuildwheel itself requires a recent Python version to run (we support th
 
 <sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br>
 <sup>² Windows arm64 support is experimental.</sup><br>
-<sup>³ Free-threaded mode requires opt-in using [`CIBW_ENABLE`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</sup><br>
+<sup>³ Free-threaded mode requires opt-in using [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</sup><br>
 <sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br>
 <sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br>
 
diff --git a/docs/contributing.md b/docs/contributing.md
index 40ebaa8b6..31c879a79 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -16,7 +16,7 @@ Everyone contributing to the cibuildwheel project is expected to follow the [PSF
 
 - `cibuildwheel` should wrap the complexity of wheel building.
 - The user interface to `cibuildwheel` is the build script (e.g. `.travis.yml`). Feature additions should not increase the complexity of this script.
-- Options should be environment variables (these lend themselves better to YML config files). They should be prefixed with `CIBW_`.
+- Options should be environment variables (these lend themselves better to YAML config files). They should be prefixed with `CIBW_`.
 - Options should be generalised to all platforms. If platform-specific options are required, they should be namespaced e.g. `CIBW_TEST_COMMAND_MACOS`
 
 Other notes:
diff --git a/docs/faq.md b/docs/faq.md
index 4db883017..5c92c02a3 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -33,7 +33,7 @@ The CPython Limited API is a subset of the Python C Extension API that's declare
 
 To create a package that builds ABI3 wheels, you'll need to configure your build backend to compile libraries correctly create wheels with the right tags. [Check this repo](https://github.com/joerick/python-abi3-package-sample) for an example of how to do this with setuptools.
 
-You could also consider running [abi3audit](https://github.com/trailofbits/abi3audit) against the produced wheels in order to check for abi3 violations or inconsistencies. You can run it alongside the default in your [CIBW_REPAIR_WHEEL_COMMAND](options.md#repair-wheel-command).
+You could also consider running [abi3audit](https://github.com/trailofbits/abi3audit) against the produced wheels in order to check for abi3 violations or inconsistencies. You can run it alongside the default in your [repair-wheel-command](options.md#repair-wheel-command).
 
 ### Packages with optional C extensions {: #optional-extensions}
 
@@ -108,7 +108,7 @@ package 'sdist' will also benefit.
 
 #### Missing build dependencies {: #cibw-options-alternatives-deps}
 
-If your build needs Python dependencies, rather than using `CIBW_BEFORE_BUILD`, it's best to add these to the
+If your build needs Python dependencies, rather than using `before-build`, it's best to add these to the
 [`build-system.requires`](https://www.python.org/dev/peps/pep-0518/#build-system-table)
 section of your pyproject.toml. For example, if your project requires Cython
 to build, your pyproject.toml might include a section like this:
@@ -129,7 +129,7 @@ You might need to run some other commands before building, like running a
 script that performs codegen or downloading some data that's not stored in
 your source tree.
 
-Rather than using `CIBW_BEFORE_ALL` or `CIBW_BEFORE_BUILD`, you could incorporate
+Rather than using `before-all` or `before-build`, you could incorporate
 these steps into your package's build process. For example, if you're using
 setuptools, you can add steps to your package's `setup.py` using a structure
 like this:
@@ -177,16 +177,16 @@ Sometimes a build will fail due to a missing dependency.
 
 **If the build is missing a Python package**, you should [add it to pyproject.toml](#cibw-options-alternatives-deps).
 
-**If you need a build tool** (e.g. cmake, automake, ninja), you can install it through a package manager like apt/yum, brew or choco, using the [`CIBW_BEFORE_ALL`](options.md#before-all) option.
+**If you need a build tool** (e.g. cmake, automake, ninja), you can install it through a package manager like apt/yum, brew or choco, using the [`before-all`](options.md#before-all) option.
 
-**If your build is linking into a native library dependency**, you can build/install that in [`CIBW_BEFORE_ALL`](options.md#before-all). However, on Linux, Mac (and Windows if you're using [delvewheel]), the library that you install will be bundled into the wheel in the [repair step]. So take care to ensure that
+**If your build is linking into a native library dependency**, you can build/install that in [`before-all`](options.md#before-all). However, on Linux, Mac (and Windows if you're using [delvewheel]), the library that you install will be bundled into the wheel in the [repair step]. So take care to ensure that
 
 - the bundled library doesn't accidentally increase the minimum system requirements (such as the minimum macOS version)
 - the bundled library matches the architecture of the wheel you're building when cross-compiling
 
 This is particularly an issue on macOS, where de facto package manager Homebrew will install libraries that are compiled for the specific version of macOS that the build machine is running, rendering the wheels useless for any previous version. And brew will not install the right arch for cross compilation of Apple Silicon wheels.
 
-For these reasons, it's strongly recommended to not use brew for native library dependencies. Instead, we recommend compiling the library yourself. If you compile in the [`CIBW_BEFORE_ALL`](options.md#before-all) step, cibuildwheel will have already set the appropriate `MACOSX_DEPLOYMENT_TARGET` env var, so the library will target the correct version of macOS.
+For these reasons, it's strongly recommended to not use brew for native library dependencies. Instead, we recommend compiling the library yourself. If you compile in the [`before-all`](options.md#before-all) step, cibuildwheel will have already set the appropriate `MACOSX_DEPLOYMENT_TARGET` env var, so the library will target the correct version of macOS.
 
 !!! tip
     For build steps, Homebrew is still a great resource - you can [look up the build formula](https://formulae.brew.sh/) and use that as a starting point.
@@ -253,9 +253,9 @@ pipx run twine upload wheelhouse/*.whl
 
 ### macOS: Passing DYLD_LIBRARY_PATH to delocate
 
-macOS has built-in [System Integrity protections](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/RuntimeProtections/RuntimeProtections.html) which limits the use of `DYLD_LIBRARY_PATH` and `LD_LIBRARY_PATH` so that it does not automatically pass to children processes. This means if you set `DYLD_LIBRARY_PATH` before running cibuildwheel, or even set it in `CIBW_ENVIRONMENT`, it will be stripped out of the environment before delocate is called.
+macOS has built-in [System Integrity protections](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/RuntimeProtections/RuntimeProtections.html) which limits the use of `DYLD_LIBRARY_PATH` and `LD_LIBRARY_PATH` so that it does not automatically pass to children processes. This means if you set `DYLD_LIBRARY_PATH` before running cibuildwheel, or even set it in `environment`, it will be stripped out of the environment before delocate is called.
 
-To work around this, use a different environment variable such as `REPAIR_LIBRARY_PATH` to store the library path, and set `DYLD_LIBRARY_PATH` in [`CIBW_REPAIR_WHEEL_COMMAND_MACOS`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command), like this:
+To work around this, use a different environment variable such as `REPAIR_LIBRARY_PATH` to store the library path, and set `DYLD_LIBRARY_PATH` in [`macos.repair-wheel-command`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command), like this:
 
 !!! tab examples "Environment variables"
 
@@ -307,7 +307,7 @@ Then cibuildwheel will detect that it's installed and use it instead. However, y
 ### macOS: Library dependencies do not satisfy target MacOS
 
 Since delocate 0.11.0 there is added verification that the library binary dependencies match the target macOS version. This is to prevent the situation where a wheel platform tag is lower than the actual minimum macOS version required by the library. To resolve this error you need to build the library to the same macOS version as the target wheel (for example using `MACOSX_DEPLOYMENT_TARGET` environment variable).
-Alternatively, you could set `MACOSX_DEPLOYMENT_TARGET` in `CIBW_ENVIRONMENT` to correctly label the wheel as incompatible with older macOS versions.
+Alternatively, you could set `MACOSX_DEPLOYMENT_TARGET` in `environment` to correctly label the wheel as incompatible with older macOS versions.
 
 This error may happen when you install a library using a package manager like Homebrew, which compiles the library for the macOS version of the build machine. This is not suitable for wheels, as the library will only work on the same macOS version as the build machine. You should compile the library yourself, or use a precompiled binary that matches the target macOS version.
 
diff --git a/docs/platforms.md b/docs/platforms.md
index ca9c0f90c..d57b1b864 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -25,11 +25,11 @@ Linux wheels are built in [`manylinux`/`musllinux` containers](https://github.co
 
 -   Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using `yum` for `manylinux2014`, `apt-get` for `manylinux_2_31`, `dnf` for `manylinux_2_28` and `apk` for `musllinux_1_1` or `musllinux_1_2`, or manually. The same goes for environment variables that are potentially needed to customize the wheel building.
 
-    `cibuildwheel` supports this by providing the [`CIBW_ENVIRONMENT`](options.md#environment) and [`CIBW_BEFORE_ALL`](options.md#before-all) options to setup the build environment inside the running container.
+    `cibuildwheel` supports this by providing the [`environment`](options.md#environment) and [`before-all`](options.md#before-all) options to setup the build environment inside the running container.
 
 -   The project directory is copied into the container as `/project`, the output directory for the wheels to be copied out is `/output`. In general, this is handled transparently by `cibuildwheel`. For a more finegrained level of control however, the root of the host file system is mounted as `/host`, allowing for example to access shared files, caches, etc. on the host file system.  Note that `/host` is not available on CircleCI and GitLab CI due to their Docker policies.
 
--   Alternative Docker images can be specified with the `CIBW_MANYLINUX_*_IMAGE`/`CIBW_MUSLLINUX_*_IMAGE` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#linux-image) for more details.
+-   Alternative Docker images can be specified with the `manylinux-*-image`/`musllinux-*-image` options to allow for a custom, preconfigured build environment for the Linux builds. See [options](options.md#linux-image) for more details.
 
 ## macOS {: #macos}
 
@@ -80,7 +80,7 @@ If you set the value lower, cibuildwheel will cap it to the lowest supported val
 
 `cibuildwheel` supports both native builds and cross-compiling between `arm64` (Apple Silicon) and `x86_64` (Intel) architectures, including the cross-compatible `universal2` format. By default, macOS builds will build a single architecture wheel, using the build machine's architecture.
 
-If you need to support both x86_64 and Apple Silicon, you can use the `CIBW_ARCHS` environment variable to specify the architectures you want to build, or the value `universal2` to build a multi-architecture wheel. cibuildwheel _will_ test x86_64 wheels (or the x86_64 slice of a `universal2` wheel) when running on Apple Silicon hardware using Rosetta 2 emulation, but it is *not* possible to test Apple Silicon wheels on x86_64 hardware.
+If you need to support both `x86_64` and Apple Silicon, you can use the [`macos.archs`](options.md#archs) setting to specify the architectures you want to build, or the value `universal2` to build a multi-architecture wheel. cibuildwheel _will_ test `x86_64` wheels (or the `x86_64` slice of a `universal2` wheel) when running on Apple Silicon hardware using Rosetta 2 emulation, but it is *not* possible to test Apple Silicon wheels on `x86_64` hardware.
 
 #### Overview of Mac architectures
 
@@ -129,8 +129,8 @@ If your CI provider doesn't offer arm64 runners yet, or you want to create `univ
 
 Regarding testing,
 
-- On an arm64 runner, it is possible to test x86_64 wheels and both parts of a universal2 wheel using Rosetta 2 emulation.
-- On an x86_64 runner, arm64 code can be compiled but it can't be tested. `cibuildwheel` will raise a warning to notify you of this - these warnings can be silenced by skipping testing on these platforms: `CIBW_TEST_SKIP: "*_arm64 *_universal2:arm64"`.
+- On an arm64 runner, it is possible to test `x86_64` wheels and both parts of a `universal2` wheel using Rosetta 2 emulation.
+- On an `x86_64` runner, arm64 code can be compiled but it can't be tested. `cibuildwheel` will raise a warning to notify you of this - these warnings can be silenced by skipping testing on these platforms: `test-skip = ["*_arm64", "*_universal2:arm64"]`.
 
 !!! note
     If your project uses **Poetry** as a build backend, cross-compiling on macOS [does not currently work](https://github.com/python-poetry/poetry/issues/7107). In some cases arm64 wheels can be built but their tags will be incorrect, with the platform tag showing `x86_64` instead of `arm64`.
@@ -150,11 +150,11 @@ In order to speed-up builds, cibuildwheel will cache the tools it needs to be re
 
 ### Windows ARM64 builds {: #windows-arm64}
 
-`cibuildwheel` supports cross-compiling `ARM64` wheels on all Windows runners, but a native ARM64 runner is required for testing. On non-native runners, tests for ARM64 wheels will be automatically skipped with a warning. Add `"*-win_arm64"` to your `CIBW_TEST_SKIP` setting to suppress the warning.
+`cibuildwheel` supports cross-compiling `ARM64` wheels on all Windows runners, but a native `ARM64` runner is required for testing. On non-native runners, tests for `ARM64` wheels will be automatically skipped with a warning. Add `"*-win_arm64"` to your `test-skip` setting to suppress the warning.
 
 Cross-compilation on Windows relies on a supported build backend. Supported backends use an environment variable to specify their target platform (the one they are compiling native modules for, as opposed to the one they are running on), which is set in [cibuildwheel's windows.py](https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/platforms/windows.py) before building. Currently, `setuptools>=65.4.1` and `setuptools_rust` are the only supported backends.
 
-By default, `ARM64` is not enabled when running on non-ARM64 runners. Use [`CIBW_ARCHS`](options.md#archs) to select it.
+By default, `ARM64` is not enabled when running on non-`ARM64` runners. Use [`CIBW_ARCHS`](options.md#archs) to select it.
 
 ## Pyodide/WebAssembly {: #pyodide}
 
@@ -170,7 +170,7 @@ You must target pyodide with `--platform pyodide` (or use `--only` on the identi
 
 ### Choosing a Pyodide version {: #pyodide-choosing-a-version}
 
-It is also possible to target a specific Pyodide version by setting the `CIBW_PYODIDE_VERSION` option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
+It is also possible to target a specific Pyodide version by setting the `pyodide-version` option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
 
 ### Running tests
 
@@ -196,7 +196,7 @@ By default, cibuildwheel will build wheels for all three of these targets.
 
 If you need to specify different compilation flags or other properties on a per-ABI or per-CPU basis, you can use [configuration overrides](configuration.md#overrides) with a `select` clause that targets the specific ABI or architecture. For example, consider the following example:
 
-```
+```toml
 [tool.cibuildwheel.ios]
 test-sources = ["tests"]
 test-requires = ["pytest"]
@@ -233,12 +233,12 @@ iOS builds support both the `pip` and `build` build frontends. In principle, sup
 
 The environment used to run builds does not inherit the full user environment - in particular, `PATH` is deliberately re-written. This is because UNIX C tooling doesn't do a great job differentiating between "macOS ARM64" and "iOS ARM64" binaries. If (for example) Homebrew is on the path when compilation commands are invoked, it's easy for a macOS version of a library to be linked into the iOS binary, rendering it unusable on iOS. To prevent this, iOS builds always force `PATH` to a "known minimal" path, that includes only the bare system utilities, and the iOS compiler toolchain.
 
-If your project requires additional tools to build (such as `cmake`, `ninja`, or `rustc`), those tools must be explicitly declared as cross-build tools using [`CIBW_XBUILD_TOOLS`](options.md#xbuild-tools). *Any* tool used by the build process must be included in the `CIBW_XBUILD_TOOLS` list, not just tools that cibuildwheel will invoke directly. For example, if your build script invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your cross-build tools list.
+If your project requires additional tools to build (such as `cmake`, `ninja`, or `rustc`), those tools must be explicitly declared as cross-build tools using [`xbuild-tools`](options.md#xbuild-tools). *Any* tool used by the build process must be included in the `xbuild-tools` list, not just tools that cibuildwheel will invoke directly. For example, if your build script invokes `cmake`, and the `cmake` script invokes `magick` to perform some image transformations, both `cmake` and `magick` must be included in your cross-build tools list.
 
 ### Tests
 
 If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices.
 
-The iOS test environment can't support running shell scripts, so the [`CIBW_TEST_COMMAND`](options.md#test-command) value must be specified as if it were a command line being passed to `python -m ...`. In addition, the project must use [`CIBW_TEST_SOURCES`](options.md#test-sources) to specify the minimum subset of files that should be copied to the test environment. This is because the test must be run "on device", and the simulator device will not have access to the local project directory.
+The iOS test environment can't support running shell scripts, so the [`test-command`](options.md#test-command) value must be specified as if it were a command line being passed to `python -m ...`. In addition, the project must use [`test-sources`](options.md#test-sources) to specify the minimum subset of files that should be copied to the test environment. This is because the test must be run "on device", and the simulator device will not have access to the local project directory.
 
-The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <CIBW_TEST_COMMAND>`.
+The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <test-command>`.

From ea08120e25fc635d7b655d2fc1d48e507f7245e6 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Fri, 30 May 2025 09:53:20 -0400
Subject: [PATCH 104/193] feat: support multiple commands on iOS (#2432)

* feat: support multiple commands on iOS

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Move split_command into a util module

* (unrelated) fix test docstring

* Implement short-circuit behaviour on test-command

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 cibuildwheel/platforms/ios.py | 100 ++++++++++++++++------------------
 cibuildwheel/util/cmd.py      |  17 +++++-
 test/test_ios.py              |  40 +++++++++++++-
 3 files changed, 102 insertions(+), 55 deletions(-)

diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 660af67c1..19ef04186 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -25,7 +25,7 @@
 from ..options import Options
 from ..selector import BuildSelector
 from ..util import resources
-from ..util.cmd import call, shell
+from ..util.cmd import call, shell, split_command
 from ..util.file import (
     CIBW_CACHE_PATH,
     copy_test_sources,
@@ -618,62 +618,58 @@ def build(options: Options, tmp_path: Path) -> None:
                         )
                         raise errors.FatalError(msg)
 
-                    test_command_parts = shlex.split(build_options.test_command)
-                    if test_command_parts[0:2] != ["python", "-m"]:
-                        first_part = test_command_parts[0]
-                        if first_part == "pytest":
-                            # pytest works exactly the same as a module, so we
-                            # can just run it as a module.
-                            log.warning(
-                                unwrap_preserving_paragraphs(f"""
-                                    iOS tests configured with a test command which doesn't start
-                                    with 'python -m'. iOS tests must execute python modules - other
-                                    entrypoints are not supported.
-
-                                    cibuildwheel will try to execute it as if it started with
-                                    'python -m'. If this works, all you need to do is add that to
-                                    your test command.
-
-                                    Test command: {build_options.test_command!r}
-                                """)
-                            )
-                        else:
-                            msg = unwrap_preserving_paragraphs(
-                                f"""
-                                    iOS tests configured with a test command which doesn't start
-                                    with 'python -m'. iOS tests must execute python modules - other
-                                    entrypoints are not supported.
-
-                                    Test command: {build_options.test_command!r}
-                                """
-                            )
-                            raise errors.FatalError(msg)
-                    else:
-                        # the testbed run command actually doesn't want the
-                        # python -m prefix - it's implicit, so we remove it
-                        # here.
-                        test_command_parts = test_command_parts[2:]
-
+                    test_command_list = shlex.split(build_options.test_command)
                     try:
-                        call(
-                            "python",
-                            testbed_path,
-                            "run",
-                            *(["--verbose"] if build_options.build_verbosity > 0 else []),
-                            "--",
-                            *test_command_parts,
-                            env=test_env,
-                        )
-                        failed = False
+                        for test_command_parts in split_command(test_command_list):
+                            match test_command_parts:
+                                case ["python", "-m", *rest]:
+                                    final_command = rest
+                                case ["pytest", *rest]:
+                                    # pytest works exactly the same as a module, so we
+                                    # can just run it as a module.
+                                    msg = unwrap_preserving_paragraphs(f"""
+                                        iOS tests configured with a test command which doesn't start
+                                        with 'python -m'. iOS tests must execute python modules - other
+                                        entrypoints are not supported.
+
+                                        cibuildwheel will try to execute it as if it started with
+                                        'python -m'. If this works, all you need to do is add that to
+                                        your test command.
+
+                                        Test command: {build_options.test_command!r}
+                                    """)
+                                    log.warning(msg)
+                                    final_command = ["pytest", *rest]
+                                case _:
+                                    msg = unwrap_preserving_paragraphs(
+                                        f"""
+                                            iOS tests configured with a test command which doesn't start
+                                            with 'python -m'. iOS tests must execute python modules - other
+                                            entrypoints are not supported.
+
+                                            Test command: {build_options.test_command!r}
+                                        """
+                                    )
+                                    raise errors.FatalError(msg)
+
+                            call(
+                                "python",
+                                testbed_path,
+                                "run",
+                                *(["--verbose"] if build_options.build_verbosity > 0 else []),
+                                "--",
+                                *final_command,
+                                env=test_env,
+                            )
                     except subprocess.CalledProcessError:
-                        failed = True
-
-                    log.step_end(success=not failed)
-
-                    if failed:
+                        # catches the first test command failure in the loop,
+                        # implementing short-circuiting
+                        log.step_end(success=False)
                         log.error(f"Test suite failed on {config.identifier}")
                         sys.exit(1)
 
+                    log.step_end()
+
             # We're all done here; move it to output (overwrite existing)
             if compatible_wheel is None:
                 output_wheel = build_options.output_dir.joinpath(built_wheel.name)
diff --git a/cibuildwheel/util/cmd.py b/cibuildwheel/util/cmd.py
index fcf725819..a528480e7 100644
--- a/cibuildwheel/util/cmd.py
+++ b/cibuildwheel/util/cmd.py
@@ -4,7 +4,7 @@
 import subprocess
 import sys
 import typing
-from collections.abc import Mapping
+from collections.abc import Iterator, Mapping
 from typing import Final, Literal
 
 from ..errors import FatalError
@@ -81,3 +81,18 @@ def shell(
     command = " ".join(commands)
     print(f"+ {command}")
     subprocess.run(command, env=env, cwd=cwd, shell=True, check=True)
+
+
+def split_command(lst: list[str]) -> Iterator[list[str]]:
+    """
+    Split a shell-style command, as returned by shlex.split, into a sequence
+    of commands, separated by '&&'.
+    """
+    items = list[str]()
+    for item in lst:
+        if item == "&&":
+            yield items
+            items = []
+        else:
+            items.append(item)
+    yield items
diff --git a/test/test_ios.py b/test/test_ios.py
index 3d1674585..b2d7e892a 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -86,7 +86,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
             "CIBW_BUILD": "cp313-*",
             "CIBW_XBUILD_TOOLS": "does-exist",
             "CIBW_TEST_SOURCES": "tests",
-            "CIBW_TEST_COMMAND": "python -m unittest discover tests test_platform.py",
+            "CIBW_TEST_COMMAND": "python -m this && python -m unittest discover tests test_platform.py",
             "CIBW_BUILD_VERBOSITY": "1",
             **build_config,
         },
@@ -102,6 +102,9 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
     captured = capfd.readouterr()
     assert "'does-exist' will be included in the cross-build environment" in captured.out
 
+    # Make sure the first command ran
+    assert "Zen of Python" in captured.out
+
 
 @pytest.mark.serial
 def test_no_test_sources(tmp_path, capfd):
@@ -134,7 +137,10 @@ def test_no_test_sources(tmp_path, capfd):
 
 
 def test_ios_testing_with_placeholder(tmp_path, capfd):
-    """Build will run tests with the {project} placeholder."""
+    """
+    Tests with the {project} placeholder are not supported on iOS, because the test command
+    is run in the simulator.
+    """
     skip_if_ios_testing_not_supported()
 
     project_dir = tmp_path / "project"
@@ -159,6 +165,36 @@ def test_ios_testing_with_placeholder(tmp_path, capfd):
     assert "iOS tests cannot use placeholders" in captured.out + captured.err
 
 
+@pytest.mark.serial
+def test_ios_test_command_short_circuit(tmp_path, capfd):
+    skip_if_ios_testing_not_supported()
+
+    project_dir = tmp_path / "project"
+    basic_project = test_projects.new_c_project()
+    basic_project.files.update(basic_project_files)
+    basic_project.generate(project_dir)
+
+    with pytest.raises(subprocess.CalledProcessError):
+        # `python -m not_a_module` will fail, so `python -m this` should not be run.
+        utils.cibuildwheel_run(
+            project_dir,
+            add_env={
+                "CIBW_PLATFORM": "ios",
+                "CIBW_BUILD": "cp313-*",
+                "CIBW_XBUILD_TOOLS": "",
+                "CIBW_TEST_SOURCES": "tests",
+                "CIBW_TEST_COMMAND": "python -m not_a_module && python -m this",
+                "CIBW_BUILD_VERBOSITY": "1",
+            },
+        )
+
+    captured = capfd.readouterr()
+
+    assert "No module named not_a_module" in captured.out + captured.err
+    # assert that `python -m this` was not run
+    assert "Zen of Python" not in captured.out + captured.err
+
+
 def test_missing_xbuild_tool(tmp_path, capfd):
     """Build will fail if xbuild-tools references a non-existent tool."""
     skip_if_ios_testing_not_supported()

From 2125d7117ef94c79a8df9e7be388c3d210a2eba0 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sat, 31 May 2025 23:36:02 -0400
Subject: [PATCH 105/193] fix: test-sources should use project dir (#2437)

* fix: test-sources should use project dir

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: fix docstring

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/platforms/ios.py     |  2 +-
 cibuildwheel/platforms/linux.py   |  2 +-
 cibuildwheel/platforms/macos.py   |  2 +-
 cibuildwheel/platforms/pyodide.py |  2 +-
 cibuildwheel/platforms/windows.py |  2 +-
 cibuildwheel/util/file.py         | 10 +++++-----
 6 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 19ef04186..1a3d2d274 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -565,7 +565,7 @@ def build(options: Options, tmp_path: Path) -> None:
                     if build_options.test_sources:
                         copy_test_sources(
                             build_options.test_sources,
-                            build_options.package_dir,
+                            Path.cwd(),
                             testbed_app_path,
                         )
                     else:
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 00cd85b2f..82a695e69 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -403,7 +403,7 @@ def build_in_container(
             if build_options.test_sources:
                 copy_test_sources(
                     build_options.test_sources,
-                    build_options.package_dir,
+                    Path.cwd(),
                     test_cwd,
                     copy_into=container.copy_into,
                 )
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 3b4534d2a..99ca915b8 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -712,7 +712,7 @@ def build(options: Options, tmp_path: Path) -> None:
                             test_cwd.mkdir()
                             copy_test_sources(
                                 build_options.test_sources,
-                                build_options.package_dir,
+                                Path.cwd(),
                                 test_cwd,
                             )
                     else:
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index 920920923..4e545260f 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -527,7 +527,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 if build_options.test_sources:
                     copy_test_sources(
                         build_options.test_sources,
-                        build_options.package_dir,
+                        Path.cwd(),
                         test_cwd,
                     )
                 else:
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 521695b60..c57a53de0 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -590,7 +590,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 if build_options.test_sources:
                     copy_test_sources(
                         build_options.test_sources,
-                        build_options.package_dir,
+                        Path.cwd(),
                         test_cwd,
                     )
                 else:
diff --git a/cibuildwheel/util/file.py b/cibuildwheel/util/file.py
index e38da01eb..c0fdc2c19 100644
--- a/cibuildwheel/util/file.py
+++ b/cibuildwheel/util/file.py
@@ -111,21 +111,21 @@ def copy_into_local(src: Path, dst: PurePath) -> None:
 
 def copy_test_sources(
     test_sources: list[str],
-    package_dir: Path,
+    project_dir: Path,
     test_dir: PurePath,
     copy_into: Callable[[Path, PurePath], None] = copy_into_local,
 ) -> None:
     """Copy the list of test sources from the package to the test directory.
 
-    :param test_sources: A list of test paths, relative to the package_dir.
-    :param package_dir: The root of the package directory.
+    :param test_sources: A list of test paths, relative to the project_dir.
+    :param project_dir: The root of the project.
     :param test_dir: The folder where test sources should be placed.
-    :param copy_info: The copy function to use. By default, does a local
+    :param copy_into: The copy function to use. By default, does a local
         filesystem copy; but an OCIContainer.copy_info method (or equivalent)
         can be provided.
     """
     for test_path in test_sources:
-        source = package_dir.resolve() / test_path
+        source = project_dir.resolve() / test_path
 
         if not source.exists():
             msg = f"Test source {test_path} does not exist."

From 2346c5f6e05f3be051cc823d2547a3ee1fd83ed5 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sun, 1 Jun 2025 06:28:20 +0100
Subject: [PATCH 106/193] Fix changelog formatting bug

---
 docs/changelog.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/docs/changelog.md b/docs/changelog.md
index d267bd3af..5a0c1777d 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -79,7 +79,6 @@ _24 March 2025_
 
 - 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
 - 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
--
 
 ### v2.23.1
 

From c90accef518b1dd0253bf43b639ce21f765d6794 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sun, 1 Jun 2025 01:34:20 -0400
Subject: [PATCH 107/193] docs: color output for docs (#2407)

* docs: color output for docs

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: use asdf (python-build)

* docs: try to fix colors

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update .pre-commit-config.yaml

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml |  3 ++-
 .readthedocs.yml        |  6 +++---
 docs/main.py            | 16 +++++++++++++++-
 docs/options.md         |  2 --
 noxfile.py              |  2 +-
 pyproject.toml          |  1 +
 6 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7831eae69..e86f06134 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -87,7 +87,8 @@ repos:
     - id: check-github-actions
     - id: check-github-workflows
     - id: check-gitlab-ci
-    - id: check-readthedocs
+    # Disabled due to schema issue for now:
+    # - id: check-readthedocs
     - id: check-travis
     - id: check-jsonschema
       name: Check projects
diff --git a/.readthedocs.yml b/.readthedocs.yml
index 60724f36e..a2dc26bb7 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -3,10 +3,10 @@
 version: 2
 
 build:
-  os: ubuntu-22.04
-  tools:
-    python: "3.12"
+  os: ubuntu-24.04
   commands:
+    - asdf install python 3.14-dev
+    - asdf global python 3.14-dev
     - asdf plugin add uv
     - asdf install uv latest
     - asdf global uv latest
diff --git a/docs/main.py b/docs/main.py
index 15d42cbb1..11556dec0 100644
--- a/docs/main.py
+++ b/docs/main.py
@@ -1,8 +1,12 @@
 import os
+import re
 import subprocess
 import sysconfig
 from typing import Any
 
+import rich.console
+import rich.text
+
 
 def define_env(env: Any) -> None:
     "Hook function for mkdocs-macros"
@@ -12,5 +16,15 @@ def subprocess_run(*args: str) -> str:
         "Run a subprocess and return the stdout"
         env = os.environ.copy()
         scripts = sysconfig.get_path("scripts")
+        env.pop("NO_COLOR", None)
         env["PATH"] = f"{scripts}{os.pathsep}{env.get('PATH', '')}"
-        return subprocess.run(args, check=True, capture_output=True, text=True, env=env).stdout
+        env["PYTHON_COLORS"] = "1"
+        output = subprocess.run(args, check=True, capture_output=True, text=True, env=env).stdout
+        rich_text = rich.text.Text.from_ansi(output)
+        console = rich.console.Console(record=True, force_terminal=True)
+        console.print(rich_text)
+        page = console.export_html(inline_styles=True)
+        result = re.search(r"<body.*?>(.*?)</body>", page, re.DOTALL | re.IGNORECASE)
+        assert result
+        txt = result.group(1).strip()
+        return txt.replace("code ", 'code class="nohighlight" ')
diff --git a/docs/options.md b/docs/options.md
index fcebb386e..3a9abf552 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1714,9 +1714,7 @@ Platform-specific environment variables are also available:<br/>
 
 ### Options
 
-```text
 « subprocess_run("cibuildwheel", "--help") »
-```
 
 ### Return codes
 
diff --git a/noxfile.py b/noxfile.py
index a31c147f6..d475c60e3 100755
--- a/noxfile.py
+++ b/noxfile.py
@@ -173,7 +173,7 @@ def bump_version(session: nox.Session) -> None:
     session.install_and_run_script("bin/bump_version.py")
 
 
-@nox.session(default=False, python="3.12")
+@nox.session(default=False)
 def docs(session: nox.Session) -> None:
     """
     Build the docs. Will serve unless --non-interactive
diff --git a/pyproject.toml b/pyproject.toml
index b52371de8..4f3721c62 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -76,6 +76,7 @@ docs = [
     "mkdocs-macros-plugin",
     "mkdocs==1.6.1",
     "pymdown-extensions",
+    "rich",
 ]
 test = [
     "build",

From e6b4e2d355ca1db112930e1d498b6bfec0281575 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 2 Jun 2025 11:37:35 -0400
Subject: [PATCH 108/193] [Bot] Update dependencies (#2439)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 README.md                                     |   4 +-
 .../resources/constraints-pyodide312.txt      |   2 +-
 .../resources/pinned_docker_images.cfg        |  42 +-
 .../python-build-standalone-releases.json     | 434 +++++++++---------
 docs/working-examples.md                      |   8 +-
 5 files changed, 245 insertions(+), 245 deletions(-)

diff --git a/README.md b/README.md
index 2b2587cc3..56b098665 100644
--- a/README.md
+++ b/README.md
@@ -179,8 +179,8 @@ Here are some repos that use cibuildwheel.
 |-----------------------------------|----|----|:------|
 | [scikit-learn][]                  | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The machine learning library. A complex but clean config using many of cibuildwheel's features to build a large project with Cython and C++ extensions.  |
 | [pytorch-fairseq][]               | ![github icon][] | ![apple icon][] ![linux icon][] | Facebook AI Research Sequence-to-Sequence Toolkit written in Python. |
-| [NumPy][]                         | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The fundamental package for scientific computing with Python. |
 | [duckdb][]                        | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an analytical in-process SQL database management system |
+| [NumPy][]                         | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The fundamental package for scientific computing with Python. |
 | [Tornado][]                       | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
 | [NCNN][]                          | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
 | [Matplotlib][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
@@ -190,8 +190,8 @@ Here are some repos that use cibuildwheel.
 
 [scikit-learn]: https://github.com/scikit-learn/scikit-learn
 [pytorch-fairseq]: https://github.com/facebookresearch/fairseq
-[NumPy]: https://github.com/numpy/numpy
 [duckdb]: https://github.com/duckdb/duckdb
+[NumPy]: https://github.com/numpy/numpy
 [Tornado]: https://github.com/tornadoweb/tornado
 [NCNN]: https://github.com/Tencent/ncnn
 [Matplotlib]: https://github.com/matplotlib/matplotlib
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 8f4a6e5c7..0fdd13116 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -81,7 +81,7 @@ rich==14.0.0
     #   pyodide-build
     #   pyodide-cli
     #   typer
-ruamel-yaml==0.18.11
+ruamel-yaml==0.18.12
     # via pyodide-build
 ruamel-yaml-clib==0.2.12
     # via ruamel-yaml
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 83a71dce2..3ba9d5b39 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.31-1
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.31-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.31-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.31-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.31-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.31-1
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.31-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.31-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.31-1
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.31-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.31-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.31-1
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.31-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.31-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.31-1
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.31-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.31-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.31-1
 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.31-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.28-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.31-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.31-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index bcbc196b7..82f072fc2 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,439 +1,439 @@
 {
   "releases": [
     {
-      "tag": "20250517",
+      "tag": "20250529",
       "assets": [
         {
-          "name": "cpython-3.10.17+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.10.17%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.17+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.11.12%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.12+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.12.10%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.3+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.13.3%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.10+20250529-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.14.0a7%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250517/cpython-3.9.22%2B20250517-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.22+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 40631cddb..0eba8f54f 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -10,8 +10,8 @@ title: Working examples
 |-----------------------------------|----|----|:------|
 | [scikit-learn][]                  | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The machine learning library. A complex but clean config using many of cibuildwheel's features to build a large project with Cython and C++ extensions.  |
 | [pytorch-fairseq][]               | ![github icon][] | ![apple icon][] ![linux icon][] | Facebook AI Research Sequence-to-Sequence Toolkit written in Python. |
-| [NumPy][]                         | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The fundamental package for scientific computing with Python. |
 | [duckdb][]                        | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an analytical in-process SQL database management system |
+| [NumPy][]                         | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The fundamental package for scientific computing with Python. |
 | [Tornado][]                       | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
 | [NCNN][]                          | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
 | [Matplotlib][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
@@ -71,8 +71,8 @@ title: Working examples
 | [mosec][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust |
 | [Picologging][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. |
 | [pybind11 cmake_example][]        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system |
-| [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
 | [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
+| [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
@@ -120,8 +120,8 @@ title: Working examples
 
 [scikit-learn]: https://github.com/scikit-learn/scikit-learn
 [pytorch-fairseq]: https://github.com/facebookresearch/fairseq
-[NumPy]: https://github.com/numpy/numpy
 [duckdb]: https://github.com/duckdb/duckdb
+[NumPy]: https://github.com/numpy/numpy
 [Tornado]: https://github.com/tornadoweb/tornado
 [NCNN]: https://github.com/Tencent/ncnn
 [Matplotlib]: https://github.com/matplotlib/matplotlib
@@ -181,8 +181,8 @@ title: Working examples
 [mosec]: https://github.com/mosecorg/mosec
 [Picologging]: https://github.com/microsoft/picologging
 [pybind11 cmake_example]: https://github.com/pybind/cmake_example
-[markupsafe]: https://github.com/pallets/markupsafe
 [Rtree]: https://github.com/Toblerity/rtree
+[markupsafe]: https://github.com/pallets/markupsafe
 [KDEpy]: https://github.com/tommyod/KDEpy
 [dd-trace-py]: https://github.com/DataDog/dd-trace-py
 [tgcalls]: https://github.com/MarshalX/tgcalls

From 4aaa823c8e73cbcc3a1a797e9d7058e39230dd3b Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Tue, 3 Jun 2025 04:35:59 +0100
Subject: [PATCH 109/193] feat: add pyodide 0.28 and pyodide-prerelease (#2431)

* Add pyodide 0.28.0a3

* Add docs and tests for 0.28
This reverts commit 2e7d716a9ecfbdac159eed798ebf1b0170e6858d.

* fix: add selector for pyodide-prerelease, and add graalpy to schema

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 bin/generate_schema.py                        |  4 +++-
 cibuildwheel/resources/build-platforms.toml   |  1 +
 .../resources/cibuildwheel.schema.json        |  6 ++++--
 cibuildwheel/selector.py                      |  9 +++++++--
 docs/options.md                               | 13 +++++++-----
 docs/platforms.md                             |  4 +++-
 test/test_pyodide.py                          |  5 ++++-
 test/utils.py                                 |  2 ++
 unit_test/build_selector_test.py              | 20 +++++++++++++++++++
 9 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index 6b98ce8ef..3ea67f378 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -33,11 +33,13 @@
     description: How to inherit the parent's value.
   enable:
     enum:
+      - cpython-experimental-riscv64
       - cpython-freethreading
       - cpython-prerelease
+      - graalpy
+      - pyodide-prerelease
       - pypy
       - pypy-eol
-      - cpython-experimental-riscv64
   description: A Python version or flavor to enable.
 additionalProperties: false
 description: cibuildwheel's settings.
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 00c555794..0102ff5e7 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -221,6 +221,7 @@ python_configurations = [
 [pyodide]
 python_configurations = [
   { identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.6", node_version = "v22" },
+  { identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.28.0a3", node_version = "v22" },
 ]
 
 [ios]
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 92113e3d7..7d6aaf10d 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -13,11 +13,13 @@
     },
     "enable": {
       "enum": [
+        "cpython-experimental-riscv64",
         "cpython-freethreading",
         "cpython-prerelease",
+        "graalpy",
+        "pyodide-prerelease",
         "pypy",
-        "pypy-eol",
-        "cpython-experimental-riscv64"
+        "pypy-eol"
       ]
     },
     "description": "A Python version or flavor to enable."
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 42f4104be..256e4fb04 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -29,12 +29,13 @@ class EnableGroup(StrEnum):
     Groups of build selectors that are not enabled by default.
     """
 
+    CPythonExperimentalRiscV64 = "cpython-experimental-riscv64"
     CPythonFreeThreading = "cpython-freethreading"
     CPythonPrerelease = "cpython-prerelease"
+    GraalPy = "graalpy"
     PyPy = "pypy"
     PyPyEoL = "pypy-eol"
-    CPythonExperimentalRiscV64 = "cpython-experimental-riscv64"
-    GraalPy = "graalpy"
+    PyodidePrerelease = "pyodide-prerelease"
 
     @classmethod
     def all_groups(cls) -> frozenset["EnableGroup"]:
@@ -98,6 +99,10 @@ def __call__(self, build_id: str) -> bool:
             return False
         if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"):
             return False
+        if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
+            build_id, "cp313-pyodide_*"
+        ):
+            return False
 
         should_build = selector_matches(self.build_config, build_id)
         should_skip = selector_matches(self.skip_config, build_id)
diff --git a/docs/options.md b/docs/options.md
index 3a9abf552..67439176f 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -58,7 +58,7 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
 | Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 |                                                                                                   |                      |
 | Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                                                                   |                      |
 | Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                                                                   | cp312-pyodide_wasm32 |
-| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator |                      |
+| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 |
 | Python 3.14   | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 |                                                                                                   |                      |
 | PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
 | PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
@@ -334,15 +334,18 @@ values are:
   are disabled by default as they can't be uploaded to PyPI and a PEP will most likely
   be required before this can happen.
 - `graalpy`: Enable GraalPy.
+- `pyodide-prerelease`: Pyodide versions that haven't released yet, if one is
+  available. Safe if you are shipping a site with an early build, not for
+  general distribution.
 - `all`: Enable all of the above.
 
 !!! caution
     `cpython-prerelease` is provided for testing purposes only. It is not
     recommended to distribute wheels built with beta releases, such as
-    uploading to PyPI.  Please _do not_ upload these wheels to PyPI, as they are
-    not guaranteed to work with the final Python release.  Once Python is ABI
-    stable and enters the release candidate phase, that version of Python will
-    become available without this flag.
+    uploading to PyPI.  Please _do not_ upload these wheels to PyPI (except for
+    pre-releases), as they are not guaranteed to work with the final Python
+    release.  Once Python is ABI stable and enters the release candidate phase,
+    that version of Python will become available without this flag.
 
 !!! note
     Free threading is experimental: [What’s New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
diff --git a/docs/platforms.md b/docs/platforms.md
index d57b1b864..438ba7607 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -170,7 +170,9 @@ You must target pyodide with `--platform pyodide` (or use `--only` on the identi
 
 ### Choosing a Pyodide version {: #pyodide-choosing-a-version}
 
-It is also possible to target a specific Pyodide version by setting the `pyodide-version` option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
+It is also possible to target a specific Pyodide version by setting the [`pyodide-version`](options.md#pyodide-version) option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
+
+If there are pre-releases available for a newer Python version, the `pyodide-prerelease` [`enable`](options.md#enable) can be used to include pre-release versions.
 
 ### Running tests
 
diff --git a/test/test_pyodide.py b/test/test_pyodide.py
index 39bc2be32..9240e8266 100644
--- a/test/test_pyodide.py
+++ b/test/test_pyodide.py
@@ -58,7 +58,7 @@ def test_pyodide_build(tmp_path, use_pyproject_toml):
     basic_project.generate(project_dir)
 
     # check for node in 1 case only to reduce CI load
-    add_env = {}
+    add_env = {"CIBW_ENABLE": "pyodide-prerelease"}
     if use_pyproject_toml:
         add_env["CIBW_TEST_COMMAND"] = f"python {{project}}/check_node.py {CIBW_CACHE_PATH}"
 
@@ -72,6 +72,7 @@ def test_pyodide_build(tmp_path, use_pyproject_toml):
     # check that the expected wheels are produced
     expected_wheels = [
         "spam-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl",
+        "spam-0.1.0-cp313-cp313-pyodide_2025_0_wasm32.whl",
     ]
 
     print("actual_wheels", actual_wheels)
@@ -130,11 +131,13 @@ def test_filter():
             add_env={
                 "CIBW_TEST_REQUIRES": "pytest",
                 "CIBW_TEST_COMMAND": "python -m pytest {project}",
+                "CIBW_ENABLE": "pyodide-prerelease",
             },
         )
         # check that the expected wheels are produced
         expected_wheels = [
             "spam-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl",
+            "spam-0.1.0-cp313-cp313-pyodide_2025_0_wasm32.whl",
         ]
         print("actual_wheels", actual_wheels)
         print("expected_wheels", expected_wheels)
diff --git a/test/utils.py b/test/utils.py
index 838e2179c..71071abe0 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -261,6 +261,8 @@ def _expected_wheels(
 
     if platform == "pyodide" and python_abi_tags is None:
         python_abi_tags = ["cp312-cp312"]
+        if EnableGroup.PyodidePrerelease in enable_groups:
+            python_abi_tags.append("cp313-cp313")
     elif platform == "ios" and python_abi_tags is None:
         python_abi_tags = ["cp313-cp313"]
     elif python_abi_tags is None:
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index 1c87c4264..0b29b3887 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -89,6 +89,26 @@ def test_build_filter_pypy_all():
     assert build_selector("pp39-manylinux_x86_64")
 
 
+def test_build_filter_pyodide_prerelease():
+    build_selector = BuildSelector(
+        build_config="*",
+        skip_config="",
+        enable=frozenset([EnableGroup.PyodidePrerelease]),
+    )
+    assert build_selector("cp312-pyodide_wasm32")
+    assert build_selector("cp313-pyodide_wasm32")
+
+
+def test_build_filter_pyodide():
+    build_selector = BuildSelector(
+        build_config="*",
+        skip_config="",
+        enable=frozenset(),
+    )
+    assert build_selector("cp312-pyodide_wasm32")
+    assert not build_selector("cp313-pyodide_wasm32")
+
+
 def test_skip():
     build_selector = BuildSelector(
         build_config="*",

From 03a2ba0bec4a240bf13197b3779b7750dd37118a Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Tue, 3 Jun 2025 11:36:14 +0800
Subject: [PATCH 110/193] docs: correct documentation suggesting iOS builds all
 platforms by default. (#2441)

---
 docs/platforms.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/platforms.md b/docs/platforms.md
index 438ba7607..84a8cb502 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -194,7 +194,7 @@ iOS is effectively 2 platforms - physical devices, and simulators. While the API
 * `arm64_iphonesimulator` (for iOS simulators running on Apple Silicon macOS machines); and
 * `x64_64_iphonesimulator` (for iOS simulators running on Intel macOS machines).
 
-By default, cibuildwheel will build wheels for all three of these targets.
+By default, cibuildwheel will build all wheels for the CPU architecture of the build machine. You can build all wheels for all architectures by specifying `--archs all`.
 
 If you need to specify different compilation flags or other properties on a per-ABI or per-CPU basis, you can use [configuration overrides](configuration.md#overrides) with a `select` clause that targets the specific ABI or architecture. For example, consider the following example:
 

From 4aa7d7e9c1abf879714fcac113f39d1b6d7e0de1 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 2 Jun 2025 23:36:28 -0400
Subject: [PATCH 111/193] [pre-commit.ci] pre-commit autoupdate (#2440)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.11 → v0.11.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.11...v0.11.12)
- [github.com/pre-commit/mirrors-mypy: v1.15.0 → v1.16.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.15.0...v1.16.0)

* chore: update some things for checks

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: fully use PEP 639 licensing

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .pre-commit-config.yaml | 4 ++--
 bin/projects.py         | 1 +
 pyproject.toml          | 4 ++--
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e86f06134..896ae38ec 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,14 +14,14 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.11
+  rev: v0.11.12
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
   - id: ruff-format
 
 - repo: https://github.com/pre-commit/mirrors-mypy
-  rev: v1.15.0
+  rev: v1.16.0
   hooks:
   - id: mypy
     name: mypy 3.11 on cibuildwheel/
diff --git a/bin/projects.py b/bin/projects.py
index 029ef6679..47a1dcb4f 100755
--- a/bin/projects.py
+++ b/bin/projects.py
@@ -135,6 +135,7 @@ def fetch_icon(icon_name: str) -> None:
 
     document = xml.dom.minidom.parseString(original_svg_data)
     svgElement = document.documentElement
+    assert svgElement is not None
     assert svgElement.nodeName == "svg"
     svgElement.setAttribute("width", "16px")
     svgElement.setAttribute("fill", "#606060")
diff --git a/pyproject.toml b/pyproject.toml
index 4f3721c62..de326f087 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,6 +8,7 @@ version = "3.0.0b4"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"
+license-files = ["LICENSE"]
 requires-python = ">=3.11"
 authors = [
     { name = "Joe Rickerby", email = "joerick@mac.com" },
@@ -25,7 +26,6 @@ keywords = [
 classifiers = [
     "Development Status :: 5 - Production/Stable",
     "Intended Audience :: Developers",
-    "License :: OSI Approved :: BSD License",
     "Natural Language :: English",
     "Programming Language :: Python :: 3",
     "Programming Language :: Python :: 3 :: Only",
@@ -102,7 +102,7 @@ addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
 junit_family = "xunit2"
 xfail_strict = true
 filterwarnings = ["error"]
-log_cli_level = "info"
+log_cli_level = "INFO"
 markers = [
     "serial: tests that must *not* be run in parallel (deselect with '-m \"not serial\"')",
 ]

From ef60c822df87f3a361002ad2b182cf4adc25da4c Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Tue, 3 Jun 2025 02:36:02 -0400
Subject: [PATCH 112/193] ci: fix gitlab Windows with --allow-downgrade

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .gitlab-ci.yml              | 2 +-
 examples/gitlab-minimal.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5ee98dd54..723400c62 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -29,7 +29,7 @@ windows:
   variables:
     PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
   before_script:
-    - choco install python -y --version 3.12.4
+    - choco install python -y --allow-downgrade --version 3.12.4
   rules:
     - if: '$CI_COMMIT_BRANCH == "main"'
       variables:
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 030237dda..cefecd2d6 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -21,7 +21,7 @@ linux:
 windows:
   image: mcr.microsoft.com/windows/servercore:1809
   before_script:
-    - choco install python -y --version 3.12.4
+    - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
     - py -m pip install cibuildwheel==3.0.0b4
   script:

From a76b6fc3389d0e1f98cb11e56df7e0f5f0ddcccb Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Tue, 3 Jun 2025 02:49:08 -0400
Subject: [PATCH 113/193] Bump version: v3.0.0b5

---
 README.md                              | 45 +++++++++-----------------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      | 14 +++++++-
 docs/faq.md                            |  6 ++--
 examples/azure-pipelines-minimal.yml   |  6 ++--
 examples/circleci-minimal.yml          |  6 ++--
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 ++--
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 +--
 pyproject.toml                         |  2 +-
 18 files changed, 53 insertions(+), 56 deletions(-)

diff --git a/README.md b/README.md
index 56b098665..63044dedc 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0b4
+        run: python -m pip install cibuildwheel==3.0.0b5
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -240,12 +240,24 @@ note to self, when doing final release, change to docs URLs in this section to t
 If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
+- ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
+
+#### v3.0.0b5
+
+_3 June 2025_
+
+- ✨ Support multiple commands on iOS, joined by `&&`, like the other platforms. (#2432)
+- ✨ Add `pyodide-prerelease` enable option, with an early build of 0.28 (Python 3.13). (#2431)
+- 🛠 test-sources now uses the `project` directory instead of the `package` directory (matching the docs). (#2437)
+- 🛠 Fixed a bug with GraalPy if vsdevcmd prints an error. Cirrus CI works again. (#2414)
+- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
+- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
 
 #### v3.0.0b4
 
 _29 May 2025_
 
-- 🛠 Dependency updates, including Python 3.14.0b2 (#2371)
+- 🛠 Dependency updates, including Python 3.14.0b2. (#2371)
 - 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
 - 📚 README table now matches docs and auto-updates. (#2427, #2428)
 
@@ -263,34 +275,7 @@ _25 May 2025_
 - ✨ Adds the [`CIBW_TEST_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/latest/options/#test-environment) option, which allows you to set environment variables for the test command. cibuildwheel now sets `PYTHONSAFEPATH=1` in test environments by default, to avoid picking up package imports from the local directory - we want to test the installed wheel, not the source tree! You can change that, or any other environment variable in the test environment using this option. (#2388)
 - ✨ Improves support for Pyodide builds and adds the [`CIBW_PYODIDE_VERSION`](https://cibuildwheel.pypa.io/en/latest/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
 
-#### v3.0.0b1
-
-_19 May 2025_
-
-- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/latest/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/latest/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out!
-- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable). (#1538)
-- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b1.
-
-    _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
-- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources), and changes the working directory for tests.
-
-    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into a temporary directory, and run the tests from there. This is required for iOS builds, but also useful for other platforms, as it allows you to test the installed wheel without any chance of accidentally importing from the source tree.
-    - ~If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).~
-    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory.
-
-- ✨ Added´ `dependency-versions` inline syntax (#2123)
-- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/latest/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28` (#2330)
-- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/latest/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
-- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/latest/options/#enable) option instead. (#2095)
-- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish (#2316)
-- 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
-- ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
-- ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
-- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`. (#2095)
-- ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
-- 📚 A reorganisation of the docs, and numerous updates (#2280)
-
-<!-- [[[end]]] (checksum: b231dd6d65d5003b403f91d925140069) -->
+<!-- [[[end]]] (checksum: c95d170eee4d70055d927723c033426a) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index e5b84fbcd..b6aac9a7f 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0b4"
+__version__ = "3.0.0b5"
diff --git a/docs/changelog.md b/docs/changelog.md
index 5a0c1777d..5032069cf 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -17,12 +17,24 @@ note to self, when doing final release, change to docs URLs in this section to t
 If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
+- ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
+
+#### v3.0.0b5
+
+_3 June 2025_
+
+- ✨ Support multiple commands on iOS, joined by `&&`, like the other platforms. (#2432)
+- ✨ Add `pyodide-prerelease` enable option, with an early build of 0.28 (Python 3.13). (#2431)
+- 🛠 test-sources now uses the `project` directory instead of the `package` directory (matching the docs). (#2437)
+- 🛠 Fixed a bug with GraalPy if vsdevcmd prints an error. Cirrus CI works again. (#2414)
+- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
+- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
 
 #### v3.0.0b4
 
 _29 May 2025_
 
-- 🛠 Dependency updates, including Python 3.14.0b2 (#2371)
+- 🛠 Dependency updates, including Python 3.14.0b2. (#2371)
 - 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
 - 📚 README table now matches docs and auto-updates. (#2427, #2428)
 
diff --git a/docs/faq.md b/docs/faq.md
index 5c92c02a3..55cd467cb 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0b4
+uses: pypa/cibuildwheel@v3.0.0b5
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0b4
+cibuildwheel==3.0.0b5
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0b4 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0b5 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 786925848..3279da865 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0b4
+        pip3 install cibuildwheel==3.0.0b5
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0b4
+        python3 -m pip install cibuildwheel==3.0.0b5
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0b4
+        pip install cibuildwheel==3.0.0b5
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 46d7ca97f..d538b6244 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b4
+            python3 -m pip install --user cibuildwheel==3.0.0b5
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b4
+            python3 -m pip install --user cibuildwheel==3.0.0b5
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0b4
+            pip3 install cibuildwheel==3.0.0b5
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index df5221d1d..4fae49cb0 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b4
+    - python -m pip install cibuildwheel==3.0.0b5
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 4b476a5c5..74b2ccd5b 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b4
+    - python -m pip install cibuildwheel==3.0.0b5
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 54d5160de..1e14c5196 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b4
+        uses: pypa/cibuildwheel@v3.0.0b5
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index ddbb1fc26..f8e278105 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b4
+        uses: pypa/cibuildwheel@v3.0.0b5
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index 6f8c4e855..cd42ab514 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0b4
+        run: pipx run cibuildwheel==3.0.0b5
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index e1308d701..df679b068 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b4
+        uses: pypa/cibuildwheel@v3.0.0b5
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index cefecd2d6..b05096ace 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0b4
+    - python -m pip install cibuildwheel==3.0.0b5
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0b4
+    - py -m pip install cibuildwheel==3.0.0b5
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0b4
+    - python3 -m pip install cibuildwheel==3.0.0b5
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 48e968046..3f0a64d49 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0b4
+    - python -m pip install cibuildwheel==3.0.0b5
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index 6d382c7ea..ddb4a3ea2 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b4
+  - python3 -m pip install cibuildwheel==3.0.0b5
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 164751168..950454a3f 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b4
+  - python3 -m pip install cibuildwheel==3.0.0b5
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 2498adef5..ceb711ab2 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0b4 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b5 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0b4 twine
+      install: python3 -m pip install cibuildwheel==3.0.0b5 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index de326f087..e6b31ad7e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0b4"
+version = "3.0.0b5"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 4e8d0757df94536205a0759b683aa9e30e26cfc5 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 3 Jun 2025 17:34:22 -0400
Subject: [PATCH 114/193] chore: touch up release script (#2442)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .pre-commit-config.yaml | 1 -
 bin/bump_version.py     | 6 +++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 896ae38ec..187531216 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -35,7 +35,6 @@ repos:
       - pygithub
       - pytest
       - rich
-      - tomli
       - tomli_w
       - types-certifi
       - types-click
diff --git a/bin/bump_version.py b/bin/bump_version.py
index 452dfa066..af2d7ae32 100755
--- a/bin/bump_version.py
+++ b/bin/bump_version.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 
 # /// script
-# dependencies = ["click", "packaging", "tomli; python_version<'3.11'"]
+# dependencies = ["click", "packaging"]
 # ///
 
 
@@ -129,8 +129,8 @@ def bump_version() -> None:
         contents = contents.replace(find, replace)
         path.write_text(contents, encoding="utf8")
 
-    print("Files updated. If you want to update the changelog as part of this")
-    print("commit, do that now.")
+    print("Files updated. If you want to update docs/changelog.md as part of")
+    print("this commit, do that now.")
     print()
 
     while input('Type "done" to continue: ').strip().lower() != "done":

From b5c33b7a092835ea3343750cbec3bf09d421a045 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 4 Jun 2025 22:20:54 +0000
Subject: [PATCH 115/193] Docs fixes (#2446)

---
 docs/options.md                  |  1 +
 docs/theme_overrides/js/theme.js | 15 +++++++++++++++
 mkdocs.yml                       |  4 ++--
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/docs/options.md b/docs/options.md
index 67439176f..bf239856f 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1791,6 +1791,7 @@ Some options support placeholders, like `{project}`, `{package}` or `{wheel}`, t
     padding-left: 10px;
     padding-right: 10px;
     line-height: normal;
+    overflow: visible;
   }
   .rst-content h3 .badges code.cmd-line:before, .rst-content h3 .badges code.toml:before, .rst-content h3 .badges code.env-var:before {
     content: ' ';
diff --git a/docs/theme_overrides/js/theme.js b/docs/theme_overrides/js/theme.js
index 4ccbe61de..f87c9f266 100644
--- a/docs/theme_overrides/js/theme.js
+++ b/docs/theme_overrides/js/theme.js
@@ -126,6 +126,21 @@ function ThemeNav () {
             });
             link.prepend(expand);
         });
+
+        // EDIT by joerick
+        //
+        // workaround a bug with the site in safari. safari navigates to the
+        // anchor before the above code has run, specifically wrap with
+        // .wy-table-responsive activates css rules that change the size of
+        // tables, making anchor points move around.
+        console.log('Document ready, checking for anchor in URL');
+        const anchorEl = document.querySelector(window.location.hash);
+        anchorEl.getBoundingClientRect(); // Force layout to ensure scrollIntoView works correctly
+        if (anchorEl) {
+          console.log('Anchor element:', anchorEl);
+          anchorEl.scrollIntoView({ behavior: 'instant', block: 'start' });
+        }
+        // end edit by joerick
     };
 
     nav.reset = function () {
diff --git a/mkdocs.yml b/mkdocs.yml
index b434ca1a1..2f38025b5 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -27,9 +27,9 @@ nav:
     - faq.md
     - cpp_standards.md
   - Reference:
-    - platforms.md
-    - configuration.md
     - options.md
+    - configuration.md
+    - platforms.md
     - working-examples.md
   - About:
     - contributing.md

From 7601bfcb45be4fcda3c61d8ec571442a0631004f Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Wed, 4 Jun 2025 18:21:09 -0400
Subject: [PATCH 116/193] [Bot] Update dependencies (#2443)

---
 cibuildwheel/resources/build-platforms.toml   |  30 ++---
 .../resources/constraints-pyodide312.txt      |   4 +-
 .../resources/constraints-pyodide313.txt      | 116 ++++++++++++++++++
 .../resources/constraints-python310.txt       |   2 +-
 .../resources/constraints-python311.txt       |   2 +-
 .../resources/constraints-python312.txt       |   2 +-
 .../resources/constraints-python313.txt       |   2 +-
 .../resources/constraints-python314.txt       |   2 +-
 .../resources/constraints-python39.txt        |   2 +-
 cibuildwheel/resources/constraints.txt        |   2 +-
 .../resources/pinned_docker_images.cfg        |  54 ++++----
 .../python-build-standalone-releases.json     |  32 ++---
 docs/working-examples.md                      |   4 +-
 13 files changed, 185 insertions(+), 69 deletions(-)
 create mode 100644 cibuildwheel/resources/constraints-pyodide313.txt

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 0102ff5e7..e018fe4d5 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -159,12 +159,12 @@ python_configurations = [
   { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
   { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
   { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
-  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
-  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
-  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
-  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
-  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
-  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
+  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
+  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
+  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
+  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
+  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
+  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
   { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
   { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
   { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
@@ -195,10 +195,10 @@ python_configurations = [
   { identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
   { identifier = "cp312-win32", version = "3.12.10", arch = "32" },
   { identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" },
-  { identifier = "cp313-win32", version = "3.13.3", arch = "32" },
-  { identifier = "cp313t-win32", version = "3.13.3", arch = "32" },
-  { identifier = "cp313-win_amd64", version = "3.13.3", arch = "64" },
-  { identifier = "cp313t-win_amd64", version = "3.13.3", arch = "64" },
+  { identifier = "cp313-win32", version = "3.13.4", arch = "32" },
+  { identifier = "cp313t-win32", version = "3.13.4", arch = "32" },
+  { identifier = "cp313-win_amd64", version = "3.13.4", arch = "64" },
+  { identifier = "cp313t-win_amd64", version = "3.13.4", arch = "64" },
   { identifier = "cp314-win32", version = "3.14.0-b2", arch = "32" },
   { identifier = "cp314t-win32", version = "3.14.0-b2", arch = "32" },
   { identifier = "cp314-win_amd64", version = "3.14.0-b2", arch = "64" },
@@ -207,8 +207,8 @@ python_configurations = [
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
   { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
-  { identifier = "cp313-win_arm64", version = "3.13.3", arch = "ARM64" },
-  { identifier = "cp313t-win_arm64", version = "3.13.3", arch = "ARM64" },
+  { identifier = "cp313-win_arm64", version = "3.13.4", arch = "ARM64" },
+  { identifier = "cp313t-win_arm64", version = "3.13.4", arch = "ARM64" },
   { identifier = "cp314-win_arm64", version = "3.14.0-b2", arch = "ARM64" },
   { identifier = "cp314t-win_arm64", version = "3.14.0-b2", arch = "ARM64" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
@@ -226,7 +226,7 @@ python_configurations = [
 
 [ios]
 python_configurations = [
-  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" },
-  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" },
-  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b6/Python-3.13-iOS-support.b6.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b8/Python-3.13-iOS-support.b8.tar.gz" },
+  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b8/Python-3.13-iOS-support.b8.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b8/Python-3.13-iOS-support.b8.tar.gz" },
 ]
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 0fdd13116..959e3c756 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -62,7 +62,7 @@ pydantic-core==2.33.2
     # via pydantic
 pygments==2.19.1
     # via rich
-pyodide-build==0.30.4
+pyodide-build==0.30.5
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
 pyodide-cli==0.3.0
     # via
@@ -94,7 +94,7 @@ typer==0.16.0
     #   auditwheel-emscripten
     #   pyodide-build
     #   pyodide-cli
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via
     #   anyio
     #   pydantic
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
new file mode 100644
index 000000000..10d9b1641
--- /dev/null
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -0,0 +1,116 @@
+# This file was autogenerated by uv via the following command:
+#    nox -s update_constraints
+annotated-types==0.7.0
+    # via pydantic
+anyio==4.9.0
+    # via httpx
+auditwheel-emscripten==0.1.0
+    # via pyodide-build
+build==1.2.2.post1
+    # via
+    #   -r .nox/update_constraints/tmp/constraints-pyodide.in
+    #   pyodide-build
+certifi==2025.4.26
+    # via
+    #   httpcore
+    #   httpx
+    #   requests
+charset-normalizer==3.4.2
+    # via requests
+click==8.1.8
+    # via
+    #   -r .nox/update_constraints/tmp/constraints-pyodide.in
+    #   typer
+distlib==0.3.9
+    # via virtualenv
+filelock==3.18.0
+    # via virtualenv
+h11==0.16.0
+    # via httpcore
+httpcore==1.0.9
+    # via httpx
+httpx==0.28.1
+    # via unearth
+idna==3.10
+    # via
+    #   anyio
+    #   httpx
+    #   requests
+leb128==1.0.8
+    # via auditwheel-emscripten
+markdown-it-py==3.0.0
+    # via rich
+mdurl==0.1.2
+    # via markdown-it-py
+packaging==25.0
+    # via
+    #   auditwheel-emscripten
+    #   build
+    #   pyodide-build
+    #   unearth
+pip==25.1.1
+    # via -r .nox/update_constraints/tmp/constraints-pyodide.in
+platformdirs==4.3.8
+    # via
+    #   pyodide-build
+    #   virtualenv
+pydantic==2.11.5
+    # via
+    #   pyodide-build
+    #   pyodide-lock
+pydantic-core==2.33.2
+    # via pydantic
+pygments==2.19.1
+    # via rich
+pyodide-build==0.30.5
+    # via -r .nox/update_constraints/tmp/constraints-pyodide.in
+pyodide-cli==0.3.0
+    # via
+    #   auditwheel-emscripten
+    #   pyodide-build
+pyodide-lock==0.1.0a7
+    # via pyodide-build
+pyproject-hooks==1.2.0
+    # via build
+requests==2.32.3
+    # via pyodide-build
+resolvelib==1.1.0
+    # via pyodide-build
+rich==14.0.0
+    # via
+    #   pyodide-build
+    #   pyodide-cli
+    #   typer
+ruamel-yaml==0.18.12
+    # via pyodide-build
+ruamel-yaml-clib==0.2.12
+    # via ruamel-yaml
+shellingham==1.5.4
+    # via typer
+sniffio==1.3.1
+    # via anyio
+typer==0.16.0
+    # via
+    #   auditwheel-emscripten
+    #   pyodide-build
+    #   pyodide-cli
+typing-extensions==4.14.0
+    # via
+    #   pydantic
+    #   pydantic-core
+    #   typer
+    #   typing-inspection
+typing-inspection==0.4.1
+    # via pydantic
+unearth==0.17.5
+    # via pyodide-build
+urllib3==2.4.0
+    # via requests
+virtualenv==20.31.2
+    # via
+    #   build
+    #   pyodide-build
+wheel==0.45.1
+    # via
+    #   auditwheel-emscripten
+    #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 0a4aff319..4a4a7ebac 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 0cff478d6..4f0b01819 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 0cff478d6..4f0b01819 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 0cff478d6..4f0b01819 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python314.txt b/cibuildwheel/resources/constraints-python314.txt
index 0cff478d6..4f0b01819 100644
--- a/cibuildwheel/resources/constraints-python314.txt
+++ b/cibuildwheel/resources/constraints-python314.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 0a4aff319..4a4a7ebac 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 0cff478d6..4f0b01819 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.13.2
+typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 3ba9d5b39..158f7a581 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.31-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.31-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.04-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.04-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.04-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.04-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.31-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.04-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.04-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.31-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.31-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.04-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.04-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.04-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.04-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.05.31-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.05.31-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.04-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.04-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.04-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.04-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.05.31-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.05.31-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.04-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.04-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.04-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.04-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.05.31-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.04-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.04-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.04-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.04-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.05.31-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.05.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.05.31-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.04-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.04-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.04-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.05.31-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.05.31-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.04-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.04-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 82f072fc2..da67af223 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -3,14 +3,14 @@
     {
       "tag": "20250529",
       "assets": [
-        {
-          "name": "cpython-3.10.17+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
-        },
         {
           "name": "cpython-3.10.17+20250529-aarch64-apple-darwin-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
         },
+        {
+          "name": "cpython-3.10.17+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
         {
           "name": "cpython-3.10.17+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
@@ -167,10 +167,22 @@
           "name": "cpython-3.12.10+20250529-i686-pc-windows-msvc-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
         },
+        {
+          "name": "cpython-3.12.10+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+        },
         {
           "name": "cpython-3.12.10+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
+        {
+          "name": "cpython-3.12.10+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.12.10+20250529-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
+        },
         {
           "name": "cpython-3.12.10+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
@@ -335,18 +347,10 @@
           "name": "cpython-3.14.0a7+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
-        {
-          "name": "cpython-3.12.10+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
-        },
         {
           "name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
-        {
-          "name": "cpython-3.12.10+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
-        },
         {
           "name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
@@ -355,10 +359,6 @@
           "name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
-        {
-          "name": "cpython-3.12.10+20250529-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
-        },
         {
           "name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 0eba8f54f..7277db74d 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -99,8 +99,8 @@ title: Working examples
 | [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
 | [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
-| [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
 | [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
+| [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
 | [Arbor][]                         | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries.  |
 | [clang-format][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. |
 | [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
@@ -209,8 +209,8 @@ title: Working examples
 [boost-histogram]: https://github.com/scikit-hep/boost-histogram
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
 [Python-WebRTC]: https://github.com/MarshalX/python-webrtc
-[pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
 [fathon]: https://github.com/stfbnc/fathon
+[pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
 [Arbor]: https://github.com/arbor-sim/arbor
 [clang-format]: https://github.com/ssciwr/clang-format-wheel
 [polaroid]: https://github.com/daggy1234/polaroid

From 31f536bdf347750243cca094ffc12035161d2108 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Thu, 5 Jun 2025 11:39:19 +0000
Subject: [PATCH 117/193] Bump version: v3.0.0rc1

---
 README.md                              | 17 ++++++++---------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  6 ++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 39 insertions(+), 34 deletions(-)

diff --git a/README.md b/README.md
index 63044dedc..142a90d00 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0b5
+        run: python -m pip install cibuildwheel==3.0.0rc1
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -242,6 +242,12 @@ If you've used previous versions of the beta:
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 - ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
 
+#### v3.0.0rc1
+
+_5 June 2025_
+
+- 🛠 Updates to dependencies including CPython 3.13.4, pyodide-build and iOS support package. (#2443)
+
 #### v3.0.0b5
 
 _3 June 2025_
@@ -268,14 +274,7 @@ _28 May 2025_
 - 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
 - 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
 
-#### v3.0.0b2
-
-_25 May 2025_
-
-- ✨ Adds the [`CIBW_TEST_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/latest/options/#test-environment) option, which allows you to set environment variables for the test command. cibuildwheel now sets `PYTHONSAFEPATH=1` in test environments by default, to avoid picking up package imports from the local directory - we want to test the installed wheel, not the source tree! You can change that, or any other environment variable in the test environment using this option. (#2388)
-- ✨ Improves support for Pyodide builds and adds the [`CIBW_PYODIDE_VERSION`](https://cibuildwheel.pypa.io/en/latest/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
-
-<!-- [[[end]]] (checksum: c95d170eee4d70055d927723c033426a) -->
+<!-- [[[end]]] (checksum: dd320058fe11f76f72506b6b1a1234ee) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index b6aac9a7f..451cd1fa2 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0b5"
+__version__ = "3.0.0rc1"
diff --git a/docs/changelog.md b/docs/changelog.md
index 5032069cf..2a17e5d57 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -19,6 +19,12 @@ If you've used previous versions of the beta:
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 - ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
 
+#### v3.0.0rc1
+
+_5 June 2025_
+
+- 🛠 Updates to dependencies including CPython 3.13.4, pyodide-build and iOS support package. (#2443)
+
 #### v3.0.0b5
 
 _3 June 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 55cd467cb..75a4480ca 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0b5
+uses: pypa/cibuildwheel@v3.0.0rc1
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0b5
+cibuildwheel==3.0.0rc1
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0b5 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0rc1 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 3279da865..e7ce192d6 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0b5
+        pip3 install cibuildwheel==3.0.0rc1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0b5
+        python3 -m pip install cibuildwheel==3.0.0rc1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0b5
+        pip install cibuildwheel==3.0.0rc1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index d538b6244..c281732e0 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b5
+            python3 -m pip install --user cibuildwheel==3.0.0rc1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0b5
+            python3 -m pip install --user cibuildwheel==3.0.0rc1
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0b5
+            pip3 install cibuildwheel==3.0.0rc1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 4fae49cb0..b3e0a98c1 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b5
+    - python -m pip install cibuildwheel==3.0.0rc1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 74b2ccd5b..09ec6af36 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0b5
+    - python -m pip install cibuildwheel==3.0.0rc1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 1e14c5196..6af80f3d6 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b5
+        uses: pypa/cibuildwheel@v3.0.0rc1
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index f8e278105..576e07f7c 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b5
+        uses: pypa/cibuildwheel@v3.0.0rc1
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index cd42ab514..f8d932b44 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0b5
+        run: pipx run cibuildwheel==3.0.0rc1
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index df679b068..ea6e84237 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0b5
+        uses: pypa/cibuildwheel@v3.0.0rc1
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index b05096ace..de2e547a6 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0b5
+    - python -m pip install cibuildwheel==3.0.0rc1
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0b5
+    - py -m pip install cibuildwheel==3.0.0rc1
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0b5
+    - python3 -m pip install cibuildwheel==3.0.0rc1
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 3f0a64d49..b53937e5a 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0b5
+    - python -m pip install cibuildwheel==3.0.0rc1
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index ddb4a3ea2..eca854347 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b5
+  - python3 -m pip install cibuildwheel==3.0.0rc1
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 950454a3f..bc861bb55 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0b5
+  - python3 -m pip install cibuildwheel==3.0.0rc1
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index ceb711ab2..810746e94 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0b5 twine
+      install: python3 -m pip install cibuildwheel==3.0.0rc1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0b5 twine
+      install: python3 -m pip install cibuildwheel==3.0.0rc1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index e6b31ad7e..a0df356fa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0b5"
+version = "3.0.0rc1"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 70c6aba90165dc247b95d228d2498b5b1ed18250 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Fri, 6 Jun 2025 00:00:58 -0400
Subject: [PATCH 118/193] [Bot] Update dependencies (#2449)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   |   8 +-
 .../python-build-standalone-releases.json     | 434 +++++++++---------
 2 files changed, 221 insertions(+), 221 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index e018fe4d5..1b28c4dcd 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -220,13 +220,13 @@ python_configurations = [
 
 [pyodide]
 python_configurations = [
-  { identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.6", node_version = "v22" },
+  { identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.7", node_version = "v22" },
   { identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.28.0a3", node_version = "v22" },
 ]
 
 [ios]
 python_configurations = [
-  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b8/Python-3.13-iOS-support.b8.tar.gz" },
-  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b8/Python-3.13-iOS-support.b8.tar.gz" },
-  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b8/Python-3.13-iOS-support.b8.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
+  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
 ]
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index da67af223..89acf8404 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,439 +1,439 @@
 {
   "releases": [
     {
-      "tag": "20250529",
+      "tag": "20250604",
       "assets": [
         {
-          "name": "cpython-3.10.17+20250529-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.17+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.10.17%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.12+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.11.12%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.10+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.12.10%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.3+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.13.3%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0a7+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.14.0a7%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.22+20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250529/cpython-3.9.22%2B20250529-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }

From 2edf5784998b7b1ab70445baced23069e839541e Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Fri, 6 Jun 2025 00:05:23 -0400
Subject: [PATCH 119/193] Bump version: v3.0.0rc2

---
 README.md                              | 17 ++++++++---------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  6 ++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 39 insertions(+), 34 deletions(-)

diff --git a/README.md b/README.md
index 142a90d00..7a1f49880 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0rc1
+        run: python -m pip install cibuildwheel==3.0.0rc2
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -242,6 +242,12 @@ If you've used previous versions of the beta:
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 - ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
 
+#### v3.0.0rc2
+
+_6 June 2025_
+
+- 🛠 Updates to dependencies including Pyodide, python-build-standalone, and iOS support package. (#2449)
+
 #### v3.0.0rc1
 
 _5 June 2025_
@@ -267,14 +273,7 @@ _29 May 2025_
 - 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
 - 📚 README table now matches docs and auto-updates. (#2427, #2428)
 
-#### v3.0.0b3
-
-_28 May 2025_
-
-- 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
-- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
-
-<!-- [[[end]]] (checksum: dd320058fe11f76f72506b6b1a1234ee) -->
+<!-- [[[end]]] (checksum: 48d3233c1809c1d7a93dc7291752b4d4) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 451cd1fa2..f7bbf541c 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0rc1"
+__version__ = "3.0.0rc2"
diff --git a/docs/changelog.md b/docs/changelog.md
index 2a17e5d57..a6e09fb7c 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -19,6 +19,12 @@ If you've used previous versions of the beta:
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 - ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
 
+#### v3.0.0rc2
+
+_6 June 2025_
+
+- 🛠 Updates to dependencies including Pyodide, python-build-standalone, and iOS support package. (#2449)
+
 #### v3.0.0rc1
 
 _5 June 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 75a4480ca..c76a90d72 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0rc1
+uses: pypa/cibuildwheel@v3.0.0rc2
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0rc1
+cibuildwheel==3.0.0rc2
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0rc1 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0rc2 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index e7ce192d6..707b857e9 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0rc1
+        pip3 install cibuildwheel==3.0.0rc2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0rc1
+        python3 -m pip install cibuildwheel==3.0.0rc2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0rc1
+        pip install cibuildwheel==3.0.0rc2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index c281732e0..118d90c32 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0rc1
+            python3 -m pip install --user cibuildwheel==3.0.0rc2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0rc1
+            python3 -m pip install --user cibuildwheel==3.0.0rc2
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0rc1
+            pip3 install cibuildwheel==3.0.0rc2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index b3e0a98c1..13ed72c64 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0rc1
+    - python -m pip install cibuildwheel==3.0.0rc2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 09ec6af36..f07686e6a 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0rc1
+    - python -m pip install cibuildwheel==3.0.0rc2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 6af80f3d6..161289440 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc1
+        uses: pypa/cibuildwheel@v3.0.0rc2
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 576e07f7c..8f1feb444 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc1
+        uses: pypa/cibuildwheel@v3.0.0rc2
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index f8d932b44..c5a555230 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0rc1
+        run: pipx run cibuildwheel==3.0.0rc2
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index ea6e84237..28cfdefdc 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc1
+        uses: pypa/cibuildwheel@v3.0.0rc2
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index de2e547a6..7ba813afb 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0rc1
+    - python -m pip install cibuildwheel==3.0.0rc2
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0rc1
+    - py -m pip install cibuildwheel==3.0.0rc2
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0rc1
+    - python3 -m pip install cibuildwheel==3.0.0rc2
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index b53937e5a..7265aef61 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0rc1
+    - python -m pip install cibuildwheel==3.0.0rc2
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index eca854347..c105fd3c4 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0rc1
+  - python3 -m pip install cibuildwheel==3.0.0rc2
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index bc861bb55..4498bc6cf 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0rc1
+  - python3 -m pip install cibuildwheel==3.0.0rc2
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 810746e94..7fb5c392a 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0rc1 twine
+      install: python3 -m pip install cibuildwheel==3.0.0rc2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0rc1 twine
+      install: python3 -m pip install cibuildwheel==3.0.0rc2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index a0df356fa..2a01f6a4e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0rc1"
+version = "3.0.0rc2"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 38dab4b641824b4d652209380802ae125b645d5b Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Fri, 6 Jun 2025 09:57:58 -0400
Subject: [PATCH 120/193] docs: warning about pyodide-version (#2450)

---
 docs/options.md | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/docs/options.md b/docs/options.md
index bf239856f..c91b0b8eb 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1253,6 +1253,12 @@ The available Pyodide versions are determined by the version of `pyodide-build`
 !!! tip
     You can set the version of `pyodide-build` using the [`dependency-versions`](#dependency-versions) option.
 
+!!! warning
+    This option is considered experimental, and might be converted to a more general mechanism in a future minor cibuildwheel release.
+
+!!! warning
+    Make sure to scope it to one specific pyodide identifier with overrides if using the `pyodide-prerelease` enable.
+
 #### Examples
 
 !!! tab examples "pyproject.toml"

From 0b6c7d0aff6bd2ab65be084efe697aa213d92ea5 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 9 Jun 2025 19:37:40 +0100
Subject: [PATCH 121/193] [pre-commit.ci] pre-commit autoupdate (#2457)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.12 → v0.11.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.12...v0.11.13)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 187531216..196a01dc3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.12
+  rev: v0.11.13
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 0fe82c588261ffd3c042a2d2e6826718b43d2767 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Mon, 9 Jun 2025 18:32:32 -0400
Subject: [PATCH 122/193] chore: order python-standalone releases for stability
 (#2456)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 bin/update_python_build_standalone.py         |  20 +-
 .../python-build-standalone-releases.json     | 432 +++++++++---------
 2 files changed, 226 insertions(+), 226 deletions(-)

diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py
index 516920144..b9ac37265 100755
--- a/bin/update_python_build_standalone.py
+++ b/bin/update_python_build_standalone.py
@@ -25,25 +25,25 @@ def main() -> None:
         f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}"
     )["assets"]
 
-    assets: list[PythonBuildStandaloneAsset] = []
+    assets = [
+        PythonBuildStandaloneAsset(name=ga["name"], url=ga["browser_download_url"])
+        for ga in github_assets
+        if ga["name"].endswith("install_only.tar.gz")
+    ]
 
-    for github_asset in github_assets:
-        name = github_asset["name"]
-        if not name.endswith("install_only.tar.gz"):
-            continue
-        url = github_asset["browser_download_url"]
-        assets.append({"name": name, "url": url})
+    # Try to keep output order stable
+    assets = sorted(assets, key=lambda x: x["name"])
 
     # Write the assets to the JSON file. One day, we might need to support
     # multiple releases, but for now, we only support the latest one
-    json_file_contents: PythonBuildStandaloneReleaseData = {
-        "releases": [
+    json_file_contents = PythonBuildStandaloneReleaseData(
+        releases=[
             {
                 "tag": latest_tag,
                 "assets": assets,
             }
         ]
-    }
+    )
 
     with PYTHON_BUILD_STANDALONE_RELEASES.open("w", encoding="utf-8") as f:
         json.dump(json_file_contents, f, indent=2)
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 89acf8404..2a680516c 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -4,436 +4,436 @@
       "tag": "20250604",
       "assets": [
         {
-          "name": "cpython-3.10.18+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
-        },
-        {
-          "name": "cpython-3.10.18+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
           "name": "cpython-3.13.4+20250604-aarch64-apple-darwin-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.10.18+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         }
       ]
     }

From 487154abbb81eef6669b826d5f71c6173f7ee56f Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Mon, 9 Jun 2025 23:15:34 -0400
Subject: [PATCH 123/193] ci: update example too

---
 examples/cirrus-ci-minimal.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index f07686e6a..cea88e78b 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -54,7 +54,7 @@ windows_x86_task:
   install_pre_requirements_script:
     - choco install -y --no-progress python3 --version 3.12.4
     - refreshenv
-    - echo PATH=%PATH% >> "%CIRRUS_ENV%"
+    - set /p=PATH=%PATH%>> "%CIRRUS_ENV%"
   <<: *BUILD_AND_STORE_WHEELS
 
 macos_arm64_task:

From adc58e93d17d805f1b5db4f6fe552509256d0a49 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Mon, 9 Jun 2025 23:39:56 -0400
Subject: [PATCH 124/193] Revert "ci: update example too"

This reverts commit 487154abbb81eef6669b826d5f71c6173f7ee56f.
---
 examples/cirrus-ci-minimal.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index cea88e78b..f07686e6a 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -54,7 +54,7 @@ windows_x86_task:
   install_pre_requirements_script:
     - choco install -y --no-progress python3 --version 3.12.4
     - refreshenv
-    - set /p=PATH=%PATH%>> "%CIRRUS_ENV%"
+    - echo PATH=%PATH% >> "%CIRRUS_ENV%"
   <<: *BUILD_AND_STORE_WHEELS
 
 macos_arm64_task:

From dcecda6962ea9dd93b87b53191758cc10dd91446 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 10 Jun 2025 01:54:45 -0400
Subject: [PATCH 125/193] chore: handle update of cog to 3.5 (#2459)

chore: handle update of cog

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .pre-commit-config.yaml | 2 +-
 README.md               | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 196a01dc3..94bf4a707 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -69,7 +69,7 @@ repos:
     pass_filenames: false
     entry: cog -c -P -r -I ./bin README.md
     files: '^(README\.md|docs/changelog\.md|docs/options\.md|bin/readme.*)$'
-    additional_dependencies: [cogapp]
+    additional_dependencies: [cogapp>=3.5]
 
 - repo: https://github.com/codespell-project/codespell
   rev: v2.4.1
diff --git a/README.md b/README.md
index 7a1f49880..67881d506 100644
--- a/README.md
+++ b/README.md
@@ -162,7 +162,7 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
 |  | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build |
 
 
-<!--[[[end]]] (checksum: 4d6a8418630e9ed43251973d93798a1b) -->
+<!--[[[end]]] (sum: TWqEGGMOnt) -->
 
 These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/).
 
@@ -273,7 +273,7 @@ _29 May 2025_
 - 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
 - 📚 README table now matches docs and auto-updates. (#2427, #2428)
 
-<!-- [[[end]]] (checksum: 48d3233c1809c1d7a93dc7291752b4d4) -->
+<!-- [[[end]]] (sum: SNMjPBgJwd) -->
 
 ---
 

From c6368be701a99f8315656f925236dbcec5b9b9c2 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Tue, 10 Jun 2025 21:48:53 +0100
Subject: [PATCH 126/193] Move to the `OS-latest` image tags on Azure Pipelines
 (#2461)

---
 azure-pipelines.yml                  | 6 +++---
 examples/azure-pipelines-minimal.yml | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index ddc9f18fa..b19c516ed 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -7,7 +7,7 @@ pr:
 jobs:
 - job: linux_311
   timeoutInMinutes: 180
-  pool: {vmImage: 'Ubuntu-22.04'}
+  pool: {vmImage: 'ubuntu-latest'}
   steps:
     - task: UsePythonVersion@0
       inputs:
@@ -25,7 +25,7 @@ jobs:
         python ./bin/run_tests.py
 
 - job: macos_311
-  pool: {vmImage: 'macOS-13'}
+  pool: {vmImage: 'macOS-latest'}
   timeoutInMinutes: 120
   steps:
     - task: UsePythonVersion@0
@@ -43,7 +43,7 @@ jobs:
         python ./bin/run_tests.py
 
 - job: windows_311
-  pool: {vmImage: 'windows-2019'}
+  pool: {vmImage: 'windows-latest'}
   timeoutInMinutes: 180
   steps:
     - task: UsePythonVersion@0
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 707b857e9..003842976 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -1,6 +1,6 @@
 jobs:
 - job: linux
-  pool: {vmImage: 'Ubuntu-20.04'}
+  pool: {vmImage: 'ubuntu-latest'}
   steps:
     - task: UsePythonVersion@0
     - bash: |
@@ -14,7 +14,7 @@ jobs:
       inputs: {pathtoPublish: 'wheelhouse'}
 
 - job: macos
-  pool: {vmImage: 'macOS-13'}
+  pool: {vmImage: 'macOS-latest'}
   steps:
     - task: UsePythonVersion@0
     - bash: |
@@ -28,7 +28,7 @@ jobs:
       inputs: {pathtoPublish: wheelhouse}
 
 - job: windows
-  pool: {vmImage: 'windows-2019'}
+  pool: {vmImage: 'windows-latest'}
   steps:
     - task: UsePythonVersion@0
     - bash: |

From 3fa7bd1e72c565f4efc61363db6b2f14dbdbb198 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 10 Jun 2025 17:57:25 -0400
Subject: [PATCH 127/193] ci: fix cirrus and reduce rebuilds (#2460)

* ci: fix Cirrus CI on Windows

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: skip unrelated changes more often

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .cirrus.yml                    | 11 +++++++----
 .github/workflows/test.yml     | 13 ++++++++++++-
 azure-pipelines.yml            | 13 ++++++++++++-
 examples/cirrus-ci-minimal.yml |  6 +++++-
 test/test_ssl.py               |  4 ++++
 5 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/.cirrus.yml b/.cirrus.yml
index 1250dacb7..02cc023bb 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,9 +1,9 @@
-only_if: changesInclude('.cirrus.yml') || ($CIRRUS_BRANCH == "main" && !changesIncludeOnly('docs/*', '.pre-commit-config.yaml')) || $CIRRUS_BRANCH =~ 'cirrus.*'
+only_if: changesInclude('.cirrus.yml') || ($CIRRUS_BRANCH == "main" && !changesIncludeOnly('.github/*', 'bin/*', 'docs/*', '.circleci/*', '.travis.yml', '.pre-commit-config.yaml', '.readthedocs.yml', 'azure-pipelines.yml', 'README.md', 'mkdocs.yml', 'noxfile.py')) || $CIRRUS_BRANCH =~ 'cirrus.*'
 
 run_tests: &RUN_TESTS
   install_cibuildwheel_script:
-    - python -m pip install dependency-groups
-    - python -m dependency_groups test | xargs python -m pip install -e.
+    - python -m pip install -U pip
+    - python -m pip install -e. --group test
   run_cibuildwheel_tests_script:
     - python ./bin/run_tests.py
 
@@ -55,9 +55,12 @@ windows_x86_task:
     memory: 8G
 
   install_pre_requirements_script:
+    - certutil -generateSSTFromWU roots.sst
+    - certutil -addstore -f root roots.sst
+    - del roots.sst
     - choco install -y --no-progress python3 --version 3.12.4
     - refreshenv
-    - echo PATH=%PATH% >> "%CIRRUS_ENV%"
+    - powershell -Command "$cleanPath = $env:PATH -replace ';+$', ''; Add-Content -Path $env:CIRRUS_ENV -Value ('PATH=' + $cleanPath)"
   <<: *RUN_TESTS
 
 macos_arm64_task:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 684c76cf4..edc55f027 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -12,8 +12,19 @@ on:
       - reopened
       - labeled
     paths-ignore:
-      - 'docs/**'
+      - .ci*
+      - bin/*
+      - docs/**
+      - examples/azure-pipelines-*
+      - examples/ci*
+      - examples/travis-ci-*
       - .pre-commit-config.yaml
+      - .readthedocs.yml
+      - .travis.yml
+      - README.md
+      - azure-pipelines.yml
+      - mkdocs.yml
+      - noxfile.py
   workflow_dispatch:
     # allow manual runs on branches without a PR
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index b19c516ed..5c244ac7e 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,8 +1,19 @@
 pr:
   paths:
     exclude:
-      - docs/*
+      - .github/**
+      - bin/*
+      - docs/**
+      - examples/github-*
+      - examples/ci*
+      - examples/travis-ci-*
+      - .ci*
       - .pre-commit-config.yaml
+      - .readthedocs.yml
+      - .travis.yml
+      - README.md
+      - mkdocs.yml
+      - noxfile.py
 
 jobs:
 - job: linux_311
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index f07686e6a..d5a934c95 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -52,9 +52,13 @@ windows_x86_task:
     memory: 4G
 
   install_pre_requirements_script:
+    - certutil -generateSSTFromWU roots.sst
+    - certutil -addstore -f root roots.sst
+    - del roots.sst
     - choco install -y --no-progress python3 --version 3.12.4
     - refreshenv
-    - echo PATH=%PATH% >> "%CIRRUS_ENV%"
+    - powershell -Command "$cleanPath = $env:PATH -replace ';+$', ''; Add-Content -Path $env:CIRRUS_ENV -Value ('PATH=' + $cleanPath)"
+
   <<: *BUILD_AND_STORE_WHEELS
 
 macos_arm64_task:
diff --git a/test/test_ssl.py b/test/test_ssl.py
index e4ff24e91..0ea958ba3 100644
--- a/test/test_ssl.py
+++ b/test/test_ssl.py
@@ -1,3 +1,4 @@
+import socket
 import textwrap
 
 from . import test_projects, utils
@@ -24,6 +25,9 @@ def test(tmp_path):
     project_dir = tmp_path / "project"
     project_with_ssl_tests.generate(project_dir)
 
+    # warm up connection
+    socket.getaddrinfo("tls-v1-2.badssl.com", 443)
+
     actual_wheels = utils.cibuildwheel_run(project_dir)
 
     expected_wheels = utils.expected_wheels("spam", "0.1.0")

From e188d9e26007031c475bb5293b90c5f386ecb439 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 10 Jun 2025 17:57:41 -0400
Subject: [PATCH 128/193] feat: remove 32-bit linux from auto arch, fix auto32
 on linux aarch64 (#2458)

* feat: remove 32-bit linux from auto arch, fix auto32 on linux aarch64

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Joe Rickerby <joerick@mac.com>

* Update cibuildwheel/architecture.py

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 cibuildwheel/architecture.py               | 92 +++++++++++++---------
 docs/options.md                            | 39 +++++++--
 test/utils.py                              |  3 +-
 unit_test/architecture_test.py             | 25 +++++-
 unit_test/main_tests/main_platform_test.py |  2 +-
 unit_test/option_prepare_test.py           |  2 +
 unit_test/options_test.py                  |  1 +
 7 files changed, 112 insertions(+), 52 deletions(-)

diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py
index 0ac2f1a1a..69c164b27 100644
--- a/cibuildwheel/architecture.py
+++ b/cibuildwheel/architecture.py
@@ -75,24 +75,24 @@ class Architecture(StrEnum):
     def parse_config(config: str, platform: PlatformName) -> "set[Architecture]":
         result = set()
         for arch_str in re.split(r"[\s,]+", config):
-            if arch_str == "auto":
-                result |= Architecture.auto_archs(platform=platform)
-            elif arch_str == "native":
-                native_arch = Architecture.native_arch(platform=platform)
-                if native_arch:
-                    result.add(native_arch)
-            elif arch_str == "all":
-                result |= Architecture.all_archs(platform=platform)
-            elif arch_str == "auto64":
-                result |= Architecture.bitness_archs(platform=platform, bitness="64")
-            elif arch_str == "auto32":
-                result |= Architecture.bitness_archs(platform=platform, bitness="32")
-            else:
-                try:
-                    result.add(Architecture(arch_str))
-                except ValueError as e:
-                    msg = f"Invalid architecture '{arch_str}'"
-                    raise errors.ConfigurationError(msg) from e
+            match arch_str:
+                case "auto":
+                    result |= Architecture.auto_archs(platform=platform)
+                case "native":
+                    if native_arch := Architecture.native_arch(platform=platform):
+                        result.add(native_arch)
+                case "all":
+                    result |= Architecture.all_archs(platform=platform)
+                case "auto64":
+                    result |= Architecture.bitness_archs(platform=platform, bitness="64")
+                case "auto32":
+                    result |= Architecture.bitness_archs(platform=platform, bitness="32")
+                case _:
+                    try:
+                        result.add(Architecture(arch_str))
+                    except ValueError as e:
+                        msg = f"Invalid architecture '{arch_str}'"
+                        raise errors.ConfigurationError(msg) from e
         return result
 
     @staticmethod
@@ -142,19 +142,12 @@ def auto_archs(platform: PlatformName) -> "set[Architecture]":
             return set()  # can't build anything on this platform
         result = {native_arch}
 
-        if platform == "linux":
-            if Architecture.x86_64 in result:
-                # x86_64 machines can run i686 containers
-                result.add(Architecture.i686)
-            elif Architecture.aarch64 in result and _check_aarch32_el0():
-                result.add(Architecture.armv7l)
-
-        elif platform == "windows" and Architecture.AMD64 in result:
-            result.add(Architecture.x86)
-
-        elif platform == "ios" and native_arch == Architecture.arm64_iphonesimulator:
-            # Also build the device wheel if we're on ARM64.
-            result.add(Architecture.arm64_iphoneos)
+        match platform:
+            case "windows" if Architecture.AMD64 in result:
+                result.add(Architecture.x86)
+            case "ios" if native_arch == Architecture.arm64_iphonesimulator:
+                # Also build the device wheel if we're on ARM64.
+                result.add(Architecture.arm64_iphoneos)
 
         return result
 
@@ -183,14 +176,35 @@ def all_archs(platform: PlatformName) -> "set[Architecture]":
 
     @staticmethod
     def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> "set[Architecture]":
-        archs_32 = {Architecture.i686, Architecture.x86, Architecture.armv7l}
-        auto_archs = Architecture.auto_archs(platform)
-
-        if bitness == "64":
-            return auto_archs - archs_32
-        if bitness == "32":
-            return auto_archs & archs_32
-        typing.assert_never(bitness)
+        # This map maps 64-bit architectures to their 32-bit equivalents.
+        archs_map = {
+            Architecture.x86_64: Architecture.i686,
+            Architecture.AMD64: Architecture.x86,
+            Architecture.aarch64: Architecture.armv7l,
+        }
+        native_arch = Architecture.native_arch(platform)
+
+        if native_arch is None:
+            return set()  # can't build anything on this platform
+
+        if native_arch == Architecture.wasm32:
+            return {native_arch} if bitness == "32" else set()
+
+        match bitness:
+            case "64":
+                return {native_arch} if native_arch not in archs_map.values() else set()
+            case "32":
+                if native_arch in archs_map.values():
+                    return {native_arch}
+                elif native_arch in archs_map and platform in {"linux", "windows"}:
+                    if native_arch == Architecture.aarch64 and not _check_aarch32_el0():
+                        # If we're on aarch64, skip if we cannot build armv7l wheels.
+                        return set()
+                    return {archs_map[native_arch]}
+                else:
+                    return set()
+            case _:
+                typing.assert_never(bitness)
 
 
 def allowed_architectures_check(
diff --git a/docs/options.md b/docs/options.md
index c91b0b8eb..bd99997cc 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -191,10 +191,10 @@ Options:
 - Windows: `AMD64` `x86` `ARM64`
 - Pyodide: `wasm32`
 - iOS: `arm64_iphoneos` `arm64_iphonesimulator` `x86_64_iphonesimulator`
-- `auto`: The default archs for your machine - see the table below.
-    - `auto64`: Just the 64-bit auto archs
-    - `auto32`: Just the 32-bit auto archs
-- `native`: the native arch of the build machine - Matches [`platform.machine()`](https://docs.python.org/3/library/platform.html#platform.machine).
+- `auto`: The recommended archs for your machine - see the table below.
+- `auto64`: The 64-bit arch(s) supported by your machine (includes device and simulator for iOS)
+- `auto32`: The 32-bit arch supported by your machine
+- `native`: the native arch of the build machine - matches [`platform.machine()`](https://docs.python.org/3/library/platform.html#platform.machine).
 - `all` : expands to all the architectures supported on this OS. You may want
   to use [`build`](#build-skip) with this option to target specific
   architectures via build selectors.
@@ -205,16 +205,34 @@ Default: `auto`
 
 | Runner | `native` | `auto` | `auto64` | `auto32` |
 |---|---|---|---|---|
-| Linux / Intel | `x86_64` | `x86_64` `i686` | `x86_64` | `i686` |
-| Windows / Intel | `AMD64` | `AMD64` `x86` | `AMD64` | `x86` |
+| Linux / Intel 64-bit | `x86_64` | `x86_64` | `x86_64` | `i686` |
+| Linux / Intel 32-bit | `i686` | `i686` |  | `i686` |
+| Linux / Arm 64-bit | `aarch64` | `aarch64` | `aarch64` | `armv7l`¹ |
+| Linux / Arm 32-bit | `armv7l` | `armv7l` | | `armv7l` |
+| Windows / Intel 64-bit | `AMD64` | `AMD64` `x86` | `AMD64` | `x86` |
+| Windows / Intel 32-bit | `x86` | `x86` | | `x86` |
 | Windows / ARM64 | `ARM64` | `ARM64` | `ARM64` | |
 | macOS / Intel | `x86_64` | `x86_64` | `x86_64` |  |
 | macOS / Apple Silicon | `arm64` | `arm64` | `arm64` |  |
 | iOS on macOS / Intel | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` | `x86_64_iphonesimulator` |  |
 | iOS on macOS / Apple Silicon | `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` | `arm64_iphoneos` `arm64_iphonesimulator` |
 
+¹: This will only be included if the runner supports it.
+
 If not listed above, `auto` is the same as `native`.
 
+!!! warning
+    The `auto` option only includes 32-bit architectures if they are
+    commonly built. `cibuildwheel` 3.0 removed 32-bit Linux builds from `auto`,
+    and a future release may remove 32-bit Windows builds from `auto` as well.
+    If you know you need them, please include `auto32`. Note that modern
+    manylinux image do not support 32-bit builds. If you want to avoid 32-bit
+    Windows builds today, feel free to use `auto64`.
+
+!!! note
+    Pyodide currently ignores the architecture setting, as it always builds for
+    `wasm32`.
+
 [setup-qemu-action]: https://github.com/docker/setup-qemu-action
 [binfmt]: https://hub.docker.com/r/tonistiigi/binfmt
 
@@ -238,6 +256,10 @@ This option can also be set using the [command-line option](#command-line)
     # On an Linux Intel runner with qemu installed, build Intel and ARM wheels
     [tool.cibuildwheel.linux]
     archs = ["auto", "aarch64"]
+
+    # Build all 32-bit and 64-bit wheels natively buildable on the image
+    [tool.cibuildwheel]
+    archs = ["auto64", "auto32"]
     ```
 
 !!! tab examples "Environment variables"
@@ -250,12 +272,13 @@ This option can also be set using the [command-line option](#command-line)
 
     # On an Linux Intel runner with qemu installed, build Intel and ARM wheels
     CIBW_ARCHS_LINUX: "auto aarch64"
+
+    # Build all 32-bit and 64-bit wheels natively buildable on the image
+    CIBW_ARCHS: "auto64 auto32"
     ```
 
     Separate multiple archs with a space.
 
-
-
     It is generally recommended to use the environment variable or
     command-line option for Linux, as selecting archs often depends
     on your specific runner having qemu installed.
diff --git a/test/utils.py b/test/utils.py
index 71071abe0..782934bdf 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -175,6 +175,7 @@ def expected_wheels(
     include_universal2: bool = False,
     single_python: bool = False,
     single_arch: bool = False,
+    full_auto: bool = False,
 ) -> list[str]:
     """
     Returns the expected wheels from a run of cibuildwheel.
@@ -194,7 +195,7 @@ def expected_wheels(
 
     architectures = [machine_arch]
     if not single_arch:
-        if platform == "linux":
+        if platform == "linux" and full_auto:
             if machine_arch == "x86_64":
                 architectures.append("i686")
             elif (
diff --git a/unit_test/architecture_test.py b/unit_test/architecture_test.py
index 934b2655d..98efe91d8 100644
--- a/unit_test/architecture_test.py
+++ b/unit_test/architecture_test.py
@@ -34,8 +34,8 @@ def test_arch_auto(platform_machine):
     arch_set = Architecture.auto_archs("linux")
     expected = {
         "32": {Architecture.i686},
-        "64": {Architecture.x86_64, Architecture.i686},
-        "arm": {Architecture.aarch64, Architecture.armv7l},
+        "64": {Architecture.x86_64},
+        "arm": {Architecture.aarch64},
     }
     assert arch_set == expected[machine_name]
 
@@ -94,5 +94,24 @@ def test_arch_auto_no_aarch32(monkeypatch):
     arch_set = Architecture.parse_config("auto64", "linux")
     assert arch_set == {Architecture.aarch64}
 
+    monkeypatch.setattr(cibuildwheel.architecture, "_check_aarch32_el0", lambda: True)
+    arch_set = Architecture.parse_config("auto32", "linux")
+    assert arch_set == {Architecture.armv7l}
+
+    monkeypatch.setattr(cibuildwheel.architecture, "_check_aarch32_el0", lambda: False)
     arch_set = Architecture.parse_config("auto32", "linux")
-    assert len(arch_set) == 0
+    assert arch_set == set()
+
+
+def test_arch_native_on_ios(monkeypatch):
+    monkeypatch.setattr(sys, "platform", "darwin")
+    monkeypatch.setattr(platform_module, "machine", lambda: "arm64")
+    arch_set = Architecture.parse_config("native", platform="ios")
+    assert arch_set == {Architecture.arm64_iphonesimulator}
+
+
+def test_arch_auto_on_ios(monkeypatch):
+    monkeypatch.setattr(sys, "platform", "darwin")
+    monkeypatch.setattr(platform_module, "machine", lambda: "arm64")
+    arch_set = Architecture.parse_config("auto", platform="ios")
+    assert arch_set == {Architecture.arm64_iphonesimulator, Architecture.arm64_iphoneos}
diff --git a/unit_test/main_tests/main_platform_test.py b/unit_test/main_tests/main_platform_test.py
index 084207368..620f86e4e 100644
--- a/unit_test/main_tests/main_platform_test.py
+++ b/unit_test/main_tests/main_platform_test.py
@@ -79,7 +79,7 @@ def test_archs_default(platform, intercepted_build_args):
     options = intercepted_build_args.args[0]
 
     if platform == "linux":
-        assert options.globals.architectures == {Architecture.x86_64, Architecture.i686}
+        assert options.globals.architectures == {Architecture.x86_64}
     elif platform == "windows":
         assert options.globals.architectures == {Architecture.AMD64, Architecture.x86}
     else:
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 34f26eee6..d2a1a0826 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -51,6 +51,7 @@ def ignore_context_call(*args, **kwargs):
 @pytest.mark.usefixtures("mock_build_container", "fake_package_dir")
 def test_build_default_launches(monkeypatch):
     monkeypatch.setattr(sys, "argv", [*sys.argv, "--platform=linux"])
+    monkeypatch.setenv("CIBW_ARCHS", "auto64 auto32")
     monkeypatch.delenv("CIBW_ENABLE", raising=False)
 
     main()
@@ -105,6 +106,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 manylinux-x86_64-image = "manylinux_2_28"
 musllinux-x86_64-image = "musllinux_1_2"
 enable = ["pypy", "pypy-eol", "graalpy", "cpython-freethreading"]
+archs = ["auto64", "auto32"]
 
 # Before Python 3.10, use manylinux2014
 [[tool.cibuildwheel.overrides]]
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index 9e5a1f089..8bf7e5691 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -25,6 +25,7 @@
 [tool.cibuildwheel]
 build = ["cp38-*", "cp313-*"]
 skip = ["*musllinux*"]
+archs = ["auto64", "auto32"]
 environment = {FOO="BAR"}
 
 test-command = "pyproject"

From aa9fe2a24edd67db40cbd394e4621a479e9e69f1 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 10 Jun 2025 18:28:34 -0400
Subject: [PATCH 129/193] ci: use uv python for docs (binary b1) (#2462)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .readthedocs.yml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.readthedocs.yml b/.readthedocs.yml
index a2dc26bb7..70d3153a3 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -5,9 +5,7 @@ version: 2
 build:
   os: ubuntu-24.04
   commands:
-    - asdf install python 3.14-dev
-    - asdf global python 3.14-dev
     - asdf plugin add uv
     - asdf install uv latest
     - asdf global uv latest
-    - NO_COLOR=1 uv run --no-dev --group docs mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html
+    - NO_COLOR=1 uv run --python 3.14 --managed-python --no-dev --group docs mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html

From 1b9a56e01487f7fd9146e622505ea22d4d35e954 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Tue, 10 Jun 2025 21:06:51 -0400
Subject: [PATCH 130/193] [Bot] Update dependencies (#2455)

---
 .../resources/constraints-pyodide312.txt      |   4 +-
 .../resources/constraints-pyodide313.txt      |   4 +-
 .../resources/constraints-python310.txt       |   2 +-
 .../resources/constraints-python39.txt        |   2 +-
 .../resources/pinned_docker_images.cfg        |  54 +--
 .../python-build-standalone-releases.json     | 432 +++++++++---------
 docs/working-examples.md                      |  12 +-
 7 files changed, 255 insertions(+), 255 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 959e3c756..b41080b7b 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -72,7 +72,7 @@ pyodide-lock==0.1.0a7
     # via pyodide-build
 pyproject-hooks==1.2.0
     # via build
-requests==2.32.3
+requests==2.32.4
     # via pyodide-build
 resolvelib==1.1.0
     # via pyodide-build
@@ -81,7 +81,7 @@ rich==14.0.0
     #   pyodide-build
     #   pyodide-cli
     #   typer
-ruamel-yaml==0.18.12
+ruamel-yaml==0.18.14
     # via pyodide-build
 ruamel-yaml-clib==0.2.12
     # via ruamel-yaml
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index 10d9b1641..b3a5ffd68 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -72,7 +72,7 @@ pyodide-lock==0.1.0a7
     # via pyodide-build
 pyproject-hooks==1.2.0
     # via build
-requests==2.32.3
+requests==2.32.4
     # via pyodide-build
 resolvelib==1.1.0
     # via pyodide-build
@@ -81,7 +81,7 @@ rich==14.0.0
     #   pyodide-build
     #   pyodide-cli
     #   typer
-ruamel-yaml==0.18.12
+ruamel-yaml==0.18.14
     # via pyodide-build
 ruamel-yaml-clib==0.2.12
     # via ruamel-yaml
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 4a4a7ebac..ee7e66578 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -30,5 +30,5 @@ typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
-zipp==3.22.0
+zipp==3.23.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 4a4a7ebac..ee7e66578 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -30,5 +30,5 @@ typing-extensions==4.14.0
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
-zipp==3.22.0
+zipp==3.23.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 158f7a581..479ffca70 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.04-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.04-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.04-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.08-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.08-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.08-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.08-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.04-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.08-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.08-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.04-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.04-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.04-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.08-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.08-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.08-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.08-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.04-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.04-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.04-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.08-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.08-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.08-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.08-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.04-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.04-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.04-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.08-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.08-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.08-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.08-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.04-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.04-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.08-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.08-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.08-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.08-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.04-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.04-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.04-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.08-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.08-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.08-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.04-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.04-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.08-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.08-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 2a680516c..2597c29ba 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -4,436 +4,436 @@
       "tag": "20250604",
       "assets": [
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
-        },
-        {
-          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
           "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
           "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 7277db74d..146ef39d8 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -71,8 +71,8 @@ title: Working examples
 | [mosec][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust |
 | [Picologging][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. |
 | [pybind11 cmake_example][]        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system |
-| [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
 | [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
+| [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
@@ -81,8 +81,8 @@ title: Working examples
 | [sourmash][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
 | [abess][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
 | [python-snappy][]                 | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library |
-| [jq.py][]                         | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
 | [cyvcf2][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
+| [jq.py][]                         | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
 | [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [Tokenizer][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
 | [iminuit][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
@@ -94,8 +94,8 @@ title: Working examples
 | [iDynTree][]                      | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 |
 | [streaming-form-data][]           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython |
 | [power-grid-model][]              | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis |
-| [bx-python][]                     | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
 | [pybase64][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
+| [bx-python][]                     | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
 | [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
 | [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
@@ -181,8 +181,8 @@ title: Working examples
 [mosec]: https://github.com/mosecorg/mosec
 [Picologging]: https://github.com/microsoft/picologging
 [pybind11 cmake_example]: https://github.com/pybind/cmake_example
-[Rtree]: https://github.com/Toblerity/rtree
 [markupsafe]: https://github.com/pallets/markupsafe
+[Rtree]: https://github.com/Toblerity/rtree
 [KDEpy]: https://github.com/tommyod/KDEpy
 [dd-trace-py]: https://github.com/DataDog/dd-trace-py
 [tgcalls]: https://github.com/MarshalX/tgcalls
@@ -191,8 +191,8 @@ title: Working examples
 [sourmash]: https://github.com/sourmash-bio/sourmash
 [abess]: https://github.com/abess-team/abess
 [python-snappy]: https://github.com/intake/python-snappy
-[jq.py]: https://github.com/mwilliamson/jq.py
 [cyvcf2]: https://github.com/brentp/cyvcf2
+[jq.py]: https://github.com/mwilliamson/jq.py
 [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [Tokenizer]: https://github.com/OpenNMT/Tokenizer
 [iminuit]: https://github.com/scikit-hep/iminuit
@@ -204,8 +204,8 @@ title: Working examples
 [iDynTree]: https://github.com/robotology/idyntree
 [streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data
 [power-grid-model]: https://github.com/PowerGridModel/power-grid-model
-[bx-python]: https://github.com/bxlab/bx-python
 [pybase64]: https://github.com/mayeut/pybase64
+[bx-python]: https://github.com/bxlab/bx-python
 [boost-histogram]: https://github.com/scikit-hep/boost-histogram
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
 [Python-WebRTC]: https://github.com/MarshalX/python-webrtc

From 3c5ff0988806752c5a6502c845f9edc2d98095d6 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Wed, 11 Jun 2025 00:57:01 -0400
Subject: [PATCH 131/193] Bump version: v3.0.0rc3

---
 README.md                              | 21 +++++++++++----------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  9 +++++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 45 insertions(+), 35 deletions(-)

diff --git a/README.md b/README.md
index 67881d506..c96e24121 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0rc2
+        run: python -m pip install cibuildwheel==3.0.0rc3
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -241,6 +241,15 @@ If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 - ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
+- ⚠️ 32-bit linux builds were removed from `"auto"` (the default), now require `"auto32"` or explicit archs, as modern manylinux images (including our new default) do not support them.
+
+
+#### v3.0.0rc3
+
+_11 June 2025_
+
+- 🛠 32-bit linux builds removed from `"auto"`, requires explicit `"auto32"`. (#2458)
+- 📚 Warn that `pyodide-version` is experimental. (#2450)
 
 #### v3.0.0rc2
 
@@ -265,15 +274,7 @@ _3 June 2025_
 - 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
 - 📚 Use Python 3.14 color output in docs CLI output. (#2407)
 
-#### v3.0.0b4
-
-_29 May 2025_
-
-- 🛠 Dependency updates, including Python 3.14.0b2. (#2371)
-- 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
-- 📚 README table now matches docs and auto-updates. (#2427, #2428)
-
-<!-- [[[end]]] (sum: SNMjPBgJwd) -->
+<!-- [[[end]]] (sum: c1HE9PQmvW) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index f7bbf541c..0b609eb38 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0rc2"
+__version__ = "3.0.0rc3"
diff --git a/docs/changelog.md b/docs/changelog.md
index a6e09fb7c..da2883a75 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -18,6 +18,15 @@ If you've used previous versions of the beta:
 - ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
 - ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
 - ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
+- ⚠️ 32-bit linux builds were removed from `"auto"` (the default), now require `"auto32"` or explicit archs, as modern manylinux images (including our new default) do not support them.
+
+
+#### v3.0.0rc3
+
+_11 June 2025_
+
+- 🛠 32-bit linux builds removed from `"auto"`, requires explicit `"auto32"`. (#2458)
+- 📚 Warn that `pyodide-version` is experimental. (#2450)
 
 #### v3.0.0rc2
 
diff --git a/docs/faq.md b/docs/faq.md
index c76a90d72..1f78958c1 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -60,7 +60,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0rc2
+uses: pypa/cibuildwheel@v3.0.0rc3
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -82,7 +82,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0rc2
+cibuildwheel==3.0.0rc3
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -247,7 +247,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0rc2 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0rc3 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 003842976..13a25dd53 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0rc2
+        pip3 install cibuildwheel==3.0.0rc3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0rc2
+        python3 -m pip install cibuildwheel==3.0.0rc3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0rc2
+        pip install cibuildwheel==3.0.0rc3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 118d90c32..53a877858 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0rc2
+            python3 -m pip install --user cibuildwheel==3.0.0rc3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0rc2
+            python3 -m pip install --user cibuildwheel==3.0.0rc3
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0rc2
+            pip3 install cibuildwheel==3.0.0rc3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 13ed72c64..4150459ad 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0rc2
+    - python -m pip install cibuildwheel==3.0.0rc3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index d5a934c95..4a006d7c0 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0rc2
+    - python -m pip install cibuildwheel==3.0.0rc3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 161289440..b06ee841a 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc2
+        uses: pypa/cibuildwheel@v3.0.0rc3
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 8f1feb444..b495817ca 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc2
+        uses: pypa/cibuildwheel@v3.0.0rc3
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index c5a555230..b63f311e4 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0rc2
+        run: pipx run cibuildwheel==3.0.0rc3
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 28cfdefdc..3c4271954 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc2
+        uses: pypa/cibuildwheel@v3.0.0rc3
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 7ba813afb..5c5f02221 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0rc2
+    - python -m pip install cibuildwheel==3.0.0rc3
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0rc2
+    - py -m pip install cibuildwheel==3.0.0rc3
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0rc2
+    - python3 -m pip install cibuildwheel==3.0.0rc3
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 7265aef61..c0b3713d5 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0rc2
+    - python -m pip install cibuildwheel==3.0.0rc3
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index c105fd3c4..a60dccead 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0rc2
+  - python3 -m pip install cibuildwheel==3.0.0rc3
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 4498bc6cf..02a56dac9 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0rc2
+  - python3 -m pip install cibuildwheel==3.0.0rc3
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 7fb5c392a..86c9ca915 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0rc2 twine
+      install: python3 -m pip install cibuildwheel==3.0.0rc3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0rc2 twine
+      install: python3 -m pip install cibuildwheel==3.0.0rc3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 2a01f6a4e..00353b23b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0rc2"
+version = "3.0.0rc3"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 6f5e480fec0d367f9230ee0be4bcb56136eeec43 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 11 Jun 2025 03:56:12 -0400
Subject: [PATCH 132/193] chore: use pip's groups in CI (#2463)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .circleci/prepare.sh |  4 ++--
 .gitlab-ci.yml       | 12 ++++++------
 .travis.yml          |  4 ++--
 azure-pipelines.yml  | 12 ++++++------
 4 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/.circleci/prepare.sh b/.circleci/prepare.sh
index 86ab4cbea..6023be5ce 100644
--- a/.circleci/prepare.sh
+++ b/.circleci/prepare.sh
@@ -10,7 +10,7 @@ fi
 
 $PYTHON --version
 $PYTHON -m venv venv
-venv/bin/python -m pip install -U pip dependency-groups
-venv/bin/python -m dependency_groups test | xargs venv/bin/python -m pip install -e.
+venv/bin/python -m pip install -U pip
+venv/bin/python -m pip install -e. --group test
 venv/bin/python -m pip freeze
 venv/bin/python --version
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 723400c62..a5a7cf483 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -20,8 +20,8 @@ linux:
   script:
     - curl -sSL https://get.docker.com/ | sh
     - docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
-    - python -m pip install dependency-groups
-    - python -m dependency_groups test | xargs python -m pip install -e. pytest-custom-exit-code
+    - python -m pip install -U pip
+    - python -m pip install -e. pytest-custom-exit-code --group test
     - python ./bin/run_tests.py
 
 windows:
@@ -35,8 +35,8 @@ windows:
       variables:
         CIBW_ENABLE: "all"
   script:
-    - py -m pip install dependency-groups
-    - py -m pip install -e. pytest-custom-exit-code $(py -m dependency_groups test)
+    - py -m pip install -U pip
+    - py -m pip install -e. pytest-custom-exit-code --group test
     - py bin\run_tests.py
   tags:
     - saas-windows-medium-amd64
@@ -50,8 +50,8 @@ macos:
       variables:
         CIBW_ENABLE: "all"
   script:
-    - python3 -m pip install dependency-groups
-    - python3 -m dependency_groups test | xargs python3 -m pip install -e. pytest-custom-exit-code
+    - python3 -m pip install -U pip
+    - python3 -m pip install -e. pytest-custom-exit-code --group test
     - python3 ./bin/run_tests.py
   tags:
     - saas-macos-medium-m1
diff --git a/.travis.yml b/.travis.yml
index d2082cc1f..7b3160a18 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -64,8 +64,8 @@ jobs:
 
 install:
 - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all; fi
-- $PYTHON -m pip install -U pip dependency-groups
-- $PYTHON -m dependency_groups test | xargs $PYTHON -m pip install -e.
+- $PYTHON -m pip install -U pip
+- $PYTHON -m pip install -e. --group test
 
 script: |
   # travis_wait disable the output while waiting
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5c244ac7e..23d6928e0 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -25,8 +25,8 @@ jobs:
         versionSpec: '3.11'
     - bash: |
         docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
-        python -m pip install dependency-groups
-        python -m dependency_groups test | xargs python -m pip install -e.
+        python -m pip install -U pip
+        python -m pip install -e. --group test
         if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
           echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
           export CIBW_ENABLE=all
@@ -43,8 +43,8 @@ jobs:
       inputs:
         versionSpec: '3.11'
     - bash: |
-        python -m pip install dependency-groups
-        python -m dependency_groups test | xargs python -m pip install -e.
+        python -m pip install -U pip
+        python -m pip install -e. --group test
         if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
           echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
           export CIBW_ENABLE=all
@@ -61,8 +61,8 @@ jobs:
       inputs:
         versionSpec: '3.11'
     - bash: |
-        python -m pip install dependency-groups
-        python -m dependency_groups test | xargs python -m pip install -e.
+        python -m pip install -U pip
+        python -m pip install -e. --group test
         if [ "$(Build.SourceBranch)" = "refs/heads/main" ]; then
           echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
           export CIBW_ENABLE=all

From ff86a6457781e53a6edbb60d3c2677c64be4f282 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 11 Jun 2025 03:57:24 -0400
Subject: [PATCH 133/193] docs: add tips for numpy (#2465)

* docs: add tips for numpy

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: update example for numpy 2.3.0

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update docs/faq.md

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 docs/faq.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/docs/faq.md b/docs/faq.md
index 1f78958c1..c3a3512bd 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -49,6 +49,56 @@ myextension = Extension(
 )
 ```
 
+### Building with NumPy
+
+If using NumPy, there are a couple of things that can help.
+
+First, if you require the `numpy` package at build-time (some binding tools, like `pybind11` and `nanobind`, do not), then the backward compatibility for your `build-backend.build-requires` is a little complicated for Python <3.9:
+
+* NumPy <1.25: You must build with the oldest version of NumPy you want to support at runtime.
+* NumPy 1.25 and 1.26: Anything you build will be compatible with 1.19+ by default, and you can set the minimum target to, for example, 1.22 with `#define NPY_TARGET_VERSION NPY_1_22_API_VERSION`.
+* NumPy 2.x: You must build with NumPy 2 to support NumPy 2; otherwise the same as 1.25+.
+
+So the rule is:
+
+* Python <3.8: Use the oldest supported NumPy (via helper `oldest-supported-numpy` if you want)
+* Python 3.9+: Use latest supported NumPy (2+).
+
+Second, there might be platforms you want to ship for that NumPy (or some other scientific Python libraries) are not shipping yet for. This is often true for beta candidates of new Python releases, for example. To work with this, you can use the Scientific Python Nightly wheels. Here's an example, depending on what frontend you use:
+
+!!! tab "pip based"
+    For frontends like `build` (the default) and `pip`:
+
+    ```toml
+    [tool.cibuildwheel]
+    environment.PIP_ONLY_BINARY = "numpy"
+    environment.PIP_PREFER_BINARY = "1"
+
+    [[tool.cibuildwheel.overrides]]
+    select = ["cp314*"]
+    inherit.environment = "append"
+    environment.PIP_EXTRA_INDEX_URL = "https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/"
+    environment.PIP_PRERELEASE = "allow"
+    ```
+
+!!! tab "uv based"
+    For frontends like `build[uv]`:
+
+    ```toml
+    [tool.cibuildwheel]
+    environment.UV_ONLY_BINARY = "numpy"
+    environment.UV_PREFER_BINARY = "1"
+
+    [[tool.cibuildwheel.overrides]]
+    select = ["cp314*"]
+    inherit.environment = "append"
+    environment.UV_INDEX = "https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/"
+    environment.UV_INDEX_STRATEGY = "unsafe-best-match"
+    environment.UV_PRERELEASE = "allow"
+    ```
+
+(Note the `*_ONLY_BINARY` variable also supports `":all:"`, and you don't need both that and `*_PREFER_BINARY`, you can use either one, depending on if you want a missing wheel to be a failure or an attempt to build in CI.)
+
 ### Automatic updates using Dependabot {: #automatic-updates}
 
 Selecting a moving target (like the latest release) is generally a bad idea in CI. If something breaks, you can't tell whether it was your code or an upstream update that caused the breakage, and in a worst-case scenario, it could occur during a release.

From a73177515a438c947d6e6e7a7356dfe67991d740 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 11 Jun 2025 08:57:43 +0100
Subject: [PATCH 134/193] Docs: mobile layout fix (#2466)

* Remove console warning

* Fix table overflow issue on mobile
---
 docs/extra.css                   |  2 +-
 docs/theme_overrides/js/theme.js | 12 +++++++-----
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/docs/extra.css b/docs/extra.css
index ecd99f667..770508a63 100644
--- a/docs/extra.css
+++ b/docs/extra.css
@@ -59,7 +59,7 @@ h1, h2, h3, h4, h5, h6 {
   left: -3.236em;
   max-width: calc(100cqw);
   width: fit-content;
-  min-width: calc(100% + 3.236em + 3.236em);
+  min-width: min(calc(100% + 3.236em + 3.236em), 100cqw);
 }
 .rst-content .section .wy-table-responsive .docutils {
   padding-left: 3.236em;
diff --git a/docs/theme_overrides/js/theme.js b/docs/theme_overrides/js/theme.js
index f87c9f266..05689756c 100644
--- a/docs/theme_overrides/js/theme.js
+++ b/docs/theme_overrides/js/theme.js
@@ -134,11 +134,13 @@ function ThemeNav () {
         // .wy-table-responsive activates css rules that change the size of
         // tables, making anchor points move around.
         console.log('Document ready, checking for anchor in URL');
-        const anchorEl = document.querySelector(window.location.hash);
-        anchorEl.getBoundingClientRect(); // Force layout to ensure scrollIntoView works correctly
-        if (anchorEl) {
-          console.log('Anchor element:', anchorEl);
-          anchorEl.scrollIntoView({ behavior: 'instant', block: 'start' });
+        if (window.location.hash) {
+            const anchorEl = document.querySelector(window.location.hash);
+            anchorEl.getBoundingClientRect(); // Force layout to ensure scrollIntoView works correctly
+            if (anchorEl) {
+                console.log('Anchor element:', anchorEl);
+                anchorEl.scrollIntoView({ behavior: 'instant', block: 'start' });
+            }
         }
         // end edit by joerick
     };

From 5f22145df44122af0f5a201f93cf0207171beca7 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Wed, 11 Jun 2025 14:02:58 +0100
Subject: [PATCH 135/193] Bump version: v3.0.0

---
 README.md                              |  86 ++++++++++++-------
 cibuildwheel/__init__.py               |   2 +-
 docs/changelog.md                      | 114 ++++++-------------------
 docs/faq.md                            |   6 +-
 examples/azure-pipelines-minimal.yml   |   6 +-
 examples/circleci-minimal.yml          |   6 +-
 examples/cirrus-ci-intel-mac.yml       |   2 +-
 examples/cirrus-ci-minimal.yml         |   2 +-
 examples/github-deploy.yml             |   2 +-
 examples/github-minimal.yml            |   2 +-
 examples/github-pipx.yml               |   2 +-
 examples/github-with-qemu.yml          |   2 +-
 examples/gitlab-minimal.yml            |   6 +-
 examples/gitlab-with-qemu.yml          |   2 +-
 examples/travis-ci-deploy.yml          |   2 +-
 examples/travis-ci-minimal.yml         |   2 +-
 examples/travis-ci-test-and-deploy.yml |   4 +-
 pyproject.toml                         |   2 +-
 18 files changed, 106 insertions(+), 144 deletions(-)

diff --git a/README.md b/README.md
index c96e24121..e15eaf6af 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0rc3
+        run: python -m pip install cibuildwheel==3.0.0
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -229,52 +229,72 @@ Changelog
 
 ### v3.0.0
 
-Not yet released, but available for testing.
-
-Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
-
-<!--
-note to self, when doing final release, change to docs URLs in this section to the stable version!
--->
-
-If you've used previous versions of the beta:
-- ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
-- ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
-- ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
-- ⚠️ 32-bit linux builds were removed from `"auto"` (the default), now require `"auto32"` or explicit archs, as modern manylinux images (including our new default) do not support them.
+_11 June 2025_
 
+See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) for more info on new features!
+
+- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/stable/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out! (#2286, #2363, #2432)
+- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable). (#1538, #2411, #2414)
+- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b2. (#2390)
+
+    _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), and changes the working directory for tests. (#2062, #2284, #2437)
+
+    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into the temporary directory we run from. This is required for iOS builds, but also useful for other platforms, as it allows you to avoid placeholders.
+    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory. (#2420)
+
+- ✨ Adds [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) inline syntax (#2122)
+- ✨ Improves support for Pyodide builds and adds the experimental [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
+- ✨ Add `pyodide-prerelease` [enable](https://cibuildwheel.pypa.io/en/stable/options/#enable) option, with an early build of 0.28 (Python 3.13). (#2431)
+- ✨ Adds the [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) option, which allows you to set environment variables for the test command. (#2388)
+- ✨ Adds the [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) option, which allows you to specify tools safe for cross-compilation. Currently only used on iOS; will be useful for Android in the future. (#2317)
+- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28`. (#2330)
+- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish. (#2316)
+- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
+- 🛠 Build verbosity settings have been reworked to have consistent meanings between build backends when non-zero. (#2339)
+- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) option instead. (#2095)
+- 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
+- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
+- ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
+- ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
+- ⚠️ 32-bit Linux wheels no longer built by default - the [arch](https://cibuildwheel.pypa.io/en/stable/options/#archs) was removed from `"auto"`. It now requires explicit `"auto32"`. Note that modern manylinux images (like the new default, `manylinux_2_28`) do not have 32-bit versions. (#2458)
+- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) to `pypy` or `pypy-eol`. (#2095)
+- ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
+- 📚 A reorganisation of the docs, and numerous updates. (#2280)
+- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
+- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
+- 📚 README table now matches docs and auto-updates. (#2427, #2428)
 
-#### v3.0.0rc3
+### v2.23.3
 
-_11 June 2025_
+_26 April 2025_
 
-- 🛠 32-bit linux builds removed from `"auto"`, requires explicit `"auto32"`. (#2458)
-- 📚 Warn that `pyodide-version` is experimental. (#2450)
+- 🛠 Dependency updates, including Python 3.13.3 (#2371)
 
-#### v3.0.0rc2
+### v2.23.2
 
-_6 June 2025_
+_24 March 2025_
 
-- 🛠 Updates to dependencies including Pyodide, python-build-standalone, and iOS support package. (#2449)
+- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
+- 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
 
-#### v3.0.0rc1
+### v2.23.1
 
-_5 June 2025_
+_15 March 2025_
 
-- 🛠 Updates to dependencies including CPython 3.13.4, pyodide-build and iOS support package. (#2443)
+- ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
+- 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
 
-#### v3.0.0b5
+### v2.23.0
 
-_3 June 2025_
+_1 March 2025_
 
-- ✨ Support multiple commands on iOS, joined by `&&`, like the other platforms. (#2432)
-- ✨ Add `pyodide-prerelease` enable option, with an early build of 0.28 (Python 3.13). (#2431)
-- 🛠 test-sources now uses the `project` directory instead of the `package` directory (matching the docs). (#2437)
-- 🛠 Fixed a bug with GraalPy if vsdevcmd prints an error. Cirrus CI works again. (#2414)
-- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
-- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
+- ✨ Adds official support for the new GitHub Actions Arm runners. In fact these worked out-of-the-box, now we include them in our tests and example configs. (#2135 via #2281)
+- ✨ Adds support for building PyPy 3.11 wheels (#2268 via #2281)
+- 🛠 Adopts the beta pypa/manylinux image for armv7l builds (#2269 via #2281)
+- 🛠 Dependency updates, including Pyodide 0.27 (#2117 and #2281)
 
-<!-- [[[end]]] (sum: c1HE9PQmvW) -->
+<!-- [[[end]]] (sum: 9FCUvIZ+dy) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 0b609eb38..528787cfc 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0rc3"
+__version__ = "3.0.0"
diff --git a/docs/changelog.md b/docs/changelog.md
index da2883a75..06356a4f6 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -6,99 +6,41 @@ title: Changelog
 
 ### v3.0.0
 
-Not yet released, but available for testing.
-
-Note - when using a beta version, be sure to check the [latest docs](https://cibuildwheel.pypa.io/en/latest/), rather than the stable version, which is still on v2.X.
-
-<!--
-note to self, when doing final release, change to docs URLs in this section to the stable version!
--->
-
-If you've used previous versions of the beta:
-- ⚠️ Previous betas of v3.0 changed the working directory for tests. This has been rolled back to the v2.x behaviour, so you might need to change configs if you adapted to the beta 1 or 2 behaviour. See [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406) for more information.
-- ⚠️ GraalPy shipped with the identifier `gp242-*` in previous betas, this has been changed to `gp311_242-*` to be consistent with other interpreters, and to fix a bug with GraalPy and project requires-python detection. If you were using GraalPy, you might need to update your config to use the new identifier.
-- ⚠️ `test-sources` now uses `project` directory instead of the `package` directory (matching the docs).
-- ⚠️ 32-bit linux builds were removed from `"auto"` (the default), now require `"auto32"` or explicit archs, as modern manylinux images (including our new default) do not support them.
-
-
-#### v3.0.0rc3
-
 _11 June 2025_
 
-- 🛠 32-bit linux builds removed from `"auto"`, requires explicit `"auto32"`. (#2458)
-- 📚 Warn that `pyodide-version` is experimental. (#2450)
-
-#### v3.0.0rc2
-
-_6 June 2025_
-
-- 🛠 Updates to dependencies including Pyodide, python-build-standalone, and iOS support package. (#2449)
-
-#### v3.0.0rc1
-
-_5 June 2025_
-
-- 🛠 Updates to dependencies including CPython 3.13.4, pyodide-build and iOS support package. (#2443)
-
-#### v3.0.0b5
-
-_3 June 2025_
-
-- ✨ Support multiple commands on iOS, joined by `&&`, like the other platforms. (#2432)
-- ✨ Add `pyodide-prerelease` enable option, with an early build of 0.28 (Python 3.13). (#2431)
-- 🛠 test-sources now uses the `project` directory instead of the `package` directory (matching the docs). (#2437)
-- 🛠 Fixed a bug with GraalPy if vsdevcmd prints an error. Cirrus CI works again. (#2414)
-- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
-- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
-
-#### v3.0.0b4
+See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) for more info on new features!
 
-_29 May 2025_
-
-- 🛠 Dependency updates, including Python 3.14.0b2. (#2371)
-- 🛠 Remove the addition of `PYTHONSAFEPATH` to `test-environment`. (#2429)
-- 📚 README table now matches docs and auto-updates. (#2427, #2428)
-
-#### v3.0.0b3
-
-_28 May 2025_
-
-- 🛠 Reverts the test working dir (when test-sources isn't set) to a temporary dir, rather than the project. (#2420)
-- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
-
-#### v3.0.0b2
-
-_25 May 2025_
-
-- ✨ Adds the [`CIBW_TEST_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/latest/options/#test-environment) option, which allows you to set environment variables for the test command. cibuildwheel now sets `PYTHONSAFEPATH=1` in test environments by default, to avoid picking up package imports from the local directory - we want to test the installed wheel, not the source tree! You can change that, or any other environment variable in the test environment using this option. (#2388)
-- ✨ Improves support for Pyodide builds and adds the [`CIBW_PYODIDE_VERSION`](https://cibuildwheel.pypa.io/en/latest/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
-
-#### v3.0.0b1
-
-_19 May 2025_
-
-- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/latest/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/latest/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out!
-- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable). (#1538)
-- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b1.
+- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/stable/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out! (#2286, #2363, #2432)
+- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable). (#1538, #2411, #2414)
+- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b2. (#2390)
 
     _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
-- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/latest/options/#test-sources), and changes the working directory for tests.
-
-    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into a temporary directory, and run the tests from there. This is required for iOS builds, but also useful for other platforms, as it allows you to test the installed wheel without any chance of accidentally importing from the source tree.
-    - ~If this option is not set, cibuildwheel will run the tests in the source tree. This is a change from the previous behavior, where cibuildwheel would run the tests from a temporary directory. We're still investigating what's best here, so if you encounter any issues with this, please let us know in [issue #2406](https://github.com/pypa/cibuildwheel/issues/2406).~
-    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory.
-
-- ✨ Added´ `dependency-versions` inline syntax (#2123)
-- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/latest/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28` (#2330)
-- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/latest/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
-- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/latest/options/#enable) option instead. (#2095)
-- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish (#2316)
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), and changes the working directory for tests. (#2062, #2284, #2437)
+
+    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into the temporary directory we run from. This is required for iOS builds, but also useful for other platforms, as it allows you to avoid placeholders.
+    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory. (#2420)
+
+- ✨ Adds [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) inline syntax (#2122)
+- ✨ Improves support for Pyodide builds and adds the experimental [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
+- ✨ Add `pyodide-prerelease` [enable](https://cibuildwheel.pypa.io/en/stable/options/#enable) option, with an early build of 0.28 (Python 3.13). (#2431)
+- ✨ Adds the [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) option, which allows you to set environment variables for the test command. (#2388)
+- ✨ Adds the [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) option, which allows you to specify tools safe for cross-compilation. Currently only used on iOS; will be useful for Android in the future. (#2317)
+- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28`. (#2330)
+- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish. (#2316)
+- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
+- 🛠 Build verbosity settings have been reworked to have consistent meanings between build backends when non-zero. (#2339)
+- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) option instead. (#2095)
 - 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
+- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
 - ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
 - ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
-- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/latest/options/#enable) to `pypy` or `pypy-eol`. (#2095)
+- ⚠️ 32-bit Linux wheels no longer built by default - the [arch](https://cibuildwheel.pypa.io/en/stable/options/#archs) was removed from `"auto"`. It now requires explicit `"auto32"`. Note that modern manylinux images (like the new default, `manylinux_2_28`) do not have 32-bit versions. (#2458)
+- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) to `pypy` or `pypy-eol`. (#2095)
 - ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
-- 📚 A reorganisation of the docs, and numerous updates (#2280)
+- 📚 A reorganisation of the docs, and numerous updates. (#2280)
+- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
+- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
+- 📚 README table now matches docs and auto-updates. (#2427, #2428)
 
 ### v2.23.3
 
@@ -574,7 +516,7 @@ _14 December 2021_
 _26 November 2021_
 
 - 📈 cibuildwheel now defaults to manylinux2014 image for linux builds, rather than manylinux2010. If you want to stick with manylinux2010, it's simple to set this using [the image options](https://cibuildwheel.pypa.io/en/stable/options/#linux-image). (#926)
-- ✨ You can now pass environment variables from the host machine into the Docker container during a Linux build. Check out [the docs for `CIBW_ENVIRONMENT_PASS_LINUX `](https://cibuildwheel.pypa.io/en/latest/options/#environment-pass) for the details. (#914)
+- ✨ You can now pass environment variables from the host machine into the Docker container during a Linux build. Check out [the docs for `CIBW_ENVIRONMENT_PASS_LINUX `](https://cibuildwheel.pypa.io/en/stable/options/#environment-pass) for the details. (#914)
 - ✨ Added support for building PyPy 3.8 wheels. (#881)
 - ✨ Added support for building Windows arm64 CPython wheels on a Windows arm64 runner. We can't test this in CI yet, so for now, this is experimental. (#920)
 - 📚 Improved the deployment documentation (#911)
@@ -605,7 +547,7 @@ _22 October 2021_
 
 - 🌟 TOML option overrides! This provides much greater flexibility in configuration via pyproject.toml. (#854)
 
-  You can now set build options for any subset of your builds using a match pattern. So, for example, you can customise CPython 3.8 builds with an override on `cp38-*` or musllinux builds by selecting `*musllinux*`. Check out [the docs](https://cibuildwheel.pypa.io/en/latest/options/#overrides) for more info on the specifics.
+  You can now set build options for any subset of your builds using a match pattern. So, for example, you can customise CPython 3.8 builds with an override on `cp38-*` or musllinux builds by selecting `*musllinux*`. Check out [the docs](https://cibuildwheel.pypa.io/en/stable/options/#overrides) for more info on the specifics.
 
 - 🛠 Added support for building PyPy wheels on macOS 11 CI runners. (#875)
 
diff --git a/docs/faq.md b/docs/faq.md
index c3a3512bd..25d9da73b 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -110,7 +110,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0rc3
+uses: pypa/cibuildwheel@v3.0.0
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -132,7 +132,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0rc3
+cibuildwheel==3.0.0
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -297,7 +297,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0rc3 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.0 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 13a25dd53..e73864f8e 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0rc3
+        pip3 install cibuildwheel==3.0.0
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0rc3
+        python3 -m pip install cibuildwheel==3.0.0
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0rc3
+        pip install cibuildwheel==3.0.0
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 53a877858..816a58135 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0rc3
+            python3 -m pip install --user cibuildwheel==3.0.0
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0rc3
+            python3 -m pip install --user cibuildwheel==3.0.0
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0rc3
+            pip3 install cibuildwheel==3.0.0
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 4150459ad..d59ecdad1 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0rc3
+    - python -m pip install cibuildwheel==3.0.0
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 4a006d7c0..0f552a15a 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0rc3
+    - python -m pip install cibuildwheel==3.0.0
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index b06ee841a..02dcf4b96 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -44,7 +44,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc3
+        uses: pypa/cibuildwheel@v3.0.0
         env:
           CIBW_PLATFORM: ${{ matrix.platform }}
           CIBW_ARCHS: ${{ matrix.archs }}
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index b495817ca..607b66f61 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc3
+        uses: pypa/cibuildwheel@v3.0.0
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index b63f311e4..c20128d85 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0rc3
+        run: pipx run cibuildwheel==3.0.0
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 3c4271954..4008a4f21 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0rc3
+        uses: pypa/cibuildwheel@v3.0.0
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 5c5f02221..1a8e054a3 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0rc3
+    - python -m pip install cibuildwheel==3.0.0
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0rc3
+    - py -m pip install cibuildwheel==3.0.0
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0rc3
+    - python3 -m pip install cibuildwheel==3.0.0
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index c0b3713d5..db86bb583 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0rc3
+    - python -m pip install cibuildwheel==3.0.0
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index a60dccead..fbf762793 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0rc3
+  - python3 -m pip install cibuildwheel==3.0.0
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 02a56dac9..796253f35 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0rc3
+  - python3 -m pip install cibuildwheel==3.0.0
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 86c9ca915..fc0dffa59 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0rc3 twine
+      install: python3 -m pip install cibuildwheel==3.0.0 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0rc3 twine
+      install: python3 -m pip install cibuildwheel==3.0.0 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 00353b23b..0b1ef884d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0rc3"
+version = "3.0.0"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 9c53c4ee46b86d79d75082a32fcfcd035bc7bbb7 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sat, 14 Jun 2025 08:58:57 -0400
Subject: [PATCH 136/193] chore: minor tweaks to generate schema script (#2467)

chore: minor tweeks to generate schema script

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 bin/generate_schema.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index 3ea67f378..cc4e8730f 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env -S uv run -q
 
 # /// script
 # dependencies = ["pyyaml"]
@@ -368,7 +368,7 @@ def as_object(d: dict[str, Any]) -> dict[str, Any]:
 if args.schemastore:
     schema["$id"] = "https://json.schemastore.org/partial-cibuildwheel.json"
     schema["description"] = (
-        "cibuildwheel's toml file, generated with ./bin/generate_schema.py --schemastore from cibuildwheel."
+        "cibuildwheel's settings. Generated with ./bin/generate_schema.py --schemastore from cibuildwheel."
     )
 
 print(json.dumps(schema, indent=2))

From 588dee0e0c7780ab3264dfd3fab3a197f50306d3 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sat, 14 Jun 2025 09:58:07 -0400
Subject: [PATCH 137/193] docs: include Windows ARM in examples (#2468)

docs: include Windows ARM
---
 README.md                     |  2 +-
 examples/github-deploy.yml    | 11 ++++-------
 examples/github-minimal.yml   |  2 +-
 examples/github-pipx.yml      |  2 +-
 examples/github-with-qemu.yml |  2 +-
 5 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/README.md b/README.md
index e15eaf6af..0abcf8f9e 100644
--- a/README.md
+++ b/README.md
@@ -88,7 +88,7 @@ jobs:
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-latest]
+        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-latest]
 
     steps:
       - uses: actions/checkout@v4
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 02dcf4b96..596ac716c 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -16,23 +16,21 @@ jobs:
     runs-on: ${{ matrix.runs-on }}
     strategy:
       matrix:
-        os: [ linux-intel, linux-arm, windows, macOS-intel, macOS-arm, iOS, pyodide ]
         include:
-          - archs: auto
-            platform: auto
           - os: linux-intel
             runs-on: ubuntu-latest
           - os: linux-arm
             runs-on: ubuntu-24.04-arm
-          - os: windows
+          - os: windows-intel
             runs-on: windows-latest
+          - os: windows-arm
+            runs-on: windows-11-arm
           - os: macos-intel
             # macos-13 was the last x86_64 runner
             runs-on: macos-13
           - os: macos-arm
             # macos-14+ (including latest) are ARM64 runners
             runs-on: macos-latest
-            archs: auto,universal2
           - os: ios
             runs-on: macos-latest
             platform: ios
@@ -46,8 +44,7 @@ jobs:
       - name: Build wheels
         uses: pypa/cibuildwheel@v3.0.0
         env:
-          CIBW_PLATFORM: ${{ matrix.platform }}
-          CIBW_ARCHS: ${{ matrix.archs }}
+          CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
         # Can also be configured directly, using `with:`
         # with:
         #   package-dir: .
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 607b66f61..4bc1fd5b8 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -9,7 +9,7 @@ jobs:
     strategy:
       matrix:
         # macos-13 is an intel runner, macos-14 is apple silicon
-        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14]
+        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-14]
 
     steps:
       - uses: actions/checkout@v4
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index c20128d85..258b562e2 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -9,7 +9,7 @@ jobs:
     strategy:
       matrix:
         # macos-13 is an intel runner, macos-14 is apple silicon
-        os: [ubuntu-latest, windows-latest, macos-13, macos-14]
+        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-14]
 
     steps:
       - uses: actions/checkout@v4
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 4008a4f21..7b376bc15 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -9,7 +9,7 @@ jobs:
     strategy:
       matrix:
         # macos-13 is an intel runner, macos-14 is apple silicon
-        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14]
+        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-14]
 
     steps:
       - uses: actions/checkout@v4

From e73749579b3347d39c4793da6f01e22ef6e4363e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 18 Jun 2025 16:08:18 -0400
Subject: [PATCH 138/193] chore(deps): bump actions/attest-build-provenance
 from 2.3.0 to 2.4.0 in the actions group (#2472)

chore(deps): bump actions/attest-build-provenance in the actions group

Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 2.3.0 to 2.4.0
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/db473fddc028af60658334401dc6fa3ffd8669fd...e8998f949152b193b063cb0ec769d69d929409be)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/release.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 34e3aacea..feb14ec60 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,7 +33,7 @@ jobs:
         path: dist
 
     - name: Generate artifact attestation for sdist and wheel
-      uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0
+      uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
       with:
         subject-path: "dist/cibuildwheel-*"
 

From 3e86452449f29075a6e1fa3a165a532effacfdab Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Fri, 20 Jun 2025 10:11:04 -0400
Subject: [PATCH 139/193] [Bot] Update dependencies (#2471)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   |  48 +-
 .../resources/constraints-pyodide312.txt      |   6 +-
 .../resources/constraints-pyodide313.txt      |   6 +-
 .../resources/pinned_docker_images.cfg        |  54 +--
 .../python-build-standalone-releases.json     | 434 +++++++++---------
 docs/working-examples.md                      |   4 +-
 6 files changed, 276 insertions(+), 276 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 1b28c4dcd..dd8aef457 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -159,18 +159,18 @@ python_configurations = [
   { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
   { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
   { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
-  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
-  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
-  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
-  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
-  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
-  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.4/python-3.13.4-macos11.pkg" },
-  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
-  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
-  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
-  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
-  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
-  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b2-macos11.pkg" },
+  { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
+  { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
+  { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
+  { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
+  { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
+  { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
+  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
+  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
+  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
+  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
+  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
+  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
   { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
   { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
   { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
@@ -195,22 +195,22 @@ python_configurations = [
   { identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
   { identifier = "cp312-win32", version = "3.12.10", arch = "32" },
   { identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" },
-  { identifier = "cp313-win32", version = "3.13.4", arch = "32" },
-  { identifier = "cp313t-win32", version = "3.13.4", arch = "32" },
-  { identifier = "cp313-win_amd64", version = "3.13.4", arch = "64" },
-  { identifier = "cp313t-win_amd64", version = "3.13.4", arch = "64" },
-  { identifier = "cp314-win32", version = "3.14.0-b2", arch = "32" },
-  { identifier = "cp314t-win32", version = "3.14.0-b2", arch = "32" },
-  { identifier = "cp314-win_amd64", version = "3.14.0-b2", arch = "64" },
-  { identifier = "cp314t-win_amd64", version = "3.14.0-b2", arch = "64" },
+  { identifier = "cp313-win32", version = "3.13.5", arch = "32" },
+  { identifier = "cp313t-win32", version = "3.13.5", arch = "32" },
+  { identifier = "cp313-win_amd64", version = "3.13.5", arch = "64" },
+  { identifier = "cp313t-win_amd64", version = "3.13.5", arch = "64" },
+  { identifier = "cp314-win32", version = "3.14.0-b3", arch = "32" },
+  { identifier = "cp314t-win32", version = "3.14.0-b3", arch = "32" },
+  { identifier = "cp314-win_amd64", version = "3.14.0-b3", arch = "64" },
+  { identifier = "cp314t-win_amd64", version = "3.14.0-b3", arch = "64" },
   { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
   { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
-  { identifier = "cp313-win_arm64", version = "3.13.4", arch = "ARM64" },
-  { identifier = "cp313t-win_arm64", version = "3.13.4", arch = "ARM64" },
-  { identifier = "cp314-win_arm64", version = "3.14.0-b2", arch = "ARM64" },
-  { identifier = "cp314t-win_arm64", version = "3.14.0-b2", arch = "ARM64" },
+  { identifier = "cp313-win_arm64", version = "3.13.5", arch = "ARM64" },
+  { identifier = "cp313t-win_arm64", version = "3.13.5", arch = "ARM64" },
+  { identifier = "cp314-win_arm64", version = "3.14.0-b3", arch = "ARM64" },
+  { identifier = "cp314t-win_arm64", version = "3.14.0-b3", arch = "ARM64" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index b41080b7b..7682c6c31 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -10,7 +10,7 @@ build==1.2.2.post1
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   pyodide-build
-certifi==2025.4.26
+certifi==2025.6.15
     # via
     #   httpcore
     #   httpx
@@ -54,7 +54,7 @@ platformdirs==4.3.8
     # via
     #   pyodide-build
     #   virtualenv
-pydantic==2.11.5
+pydantic==2.11.7
     # via
     #   pyodide-build
     #   pyodide-lock
@@ -105,7 +105,7 @@ typing-inspection==0.4.1
     # via pydantic
 unearth==0.17.5
     # via pyodide-build
-urllib3==2.4.0
+urllib3==2.5.0
     # via requests
 virtualenv==20.31.2
     # via
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index b3a5ffd68..4bf810856 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -10,7 +10,7 @@ build==1.2.2.post1
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   pyodide-build
-certifi==2025.4.26
+certifi==2025.6.15
     # via
     #   httpcore
     #   httpx
@@ -54,7 +54,7 @@ platformdirs==4.3.8
     # via
     #   pyodide-build
     #   virtualenv
-pydantic==2.11.5
+pydantic==2.11.7
     # via
     #   pyodide-build
     #   pyodide-lock
@@ -104,7 +104,7 @@ typing-inspection==0.4.1
     # via pydantic
 unearth==0.17.5
     # via pyodide-build
-urllib3==2.4.0
+urllib3==2.5.0
     # via requests
 virtualenv==20.31.2
     # via
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 479ffca70..a174458ed 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.08-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.08-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.08-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.18-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.18-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.18-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.18-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.08-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.18-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.18-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.08-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.08-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.08-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.18-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.18-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.18-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.18-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.08-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.08-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.08-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.18-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.18-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.18-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.18-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.08-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.08-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.08-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.18-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.18-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.18-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.18-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.08-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.08-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.18-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.18-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.18-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.18-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.08-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.08-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.08-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.18-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.18-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.18-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.08-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.08-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.18-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.18-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 2597c29ba..73746ce4e 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,439 +1,439 @@
 {
   "releases": [
     {
-      "tag": "20250604",
+      "tag": "20250612",
       "assets": [
         {
-          "name": "cpython-3.10.18+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.10.18%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.11.13%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.12.11%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.4+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.13.4%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b1+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.14.0b1%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b2+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250604/cpython-3.9.23%2B20250604-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 146ef39d8..5bd523ae3 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -71,8 +71,8 @@ title: Working examples
 | [mosec][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust |
 | [Picologging][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. |
 | [pybind11 cmake_example][]        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system |
-| [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
 | [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
+| [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
@@ -181,8 +181,8 @@ title: Working examples
 [mosec]: https://github.com/mosecorg/mosec
 [Picologging]: https://github.com/microsoft/picologging
 [pybind11 cmake_example]: https://github.com/pybind/cmake_example
-[markupsafe]: https://github.com/pallets/markupsafe
 [Rtree]: https://github.com/Toblerity/rtree
+[markupsafe]: https://github.com/pallets/markupsafe
 [KDEpy]: https://github.com/tommyod/KDEpy
 [dd-trace-py]: https://github.com/DataDog/dd-trace-py
 [tgcalls]: https://github.com/MarshalX/tgcalls

From e69b5532ab01c9d7c73e8e376a4e1219307cd4bd Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 23 Jun 2025 13:08:10 -0400
Subject: [PATCH 140/193] [Bot] Update dependencies (#2473)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |  2 +-
 .../resources/constraints-pyodide313.txt      |  2 +-
 .../resources/pinned_docker_images.cfg        | 54 +++++++++----------
 docs/working-examples.md                      |  4 +-
 4 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 7682c6c31..eca0ceee8 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -60,7 +60,7 @@ pydantic==2.11.7
     #   pyodide-lock
 pydantic-core==2.33.2
     # via pydantic
-pygments==2.19.1
+pygments==2.19.2
     # via rich
 pyodide-build==0.30.5
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index 4bf810856..32973e950 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -60,7 +60,7 @@ pydantic==2.11.7
     #   pyodide-lock
 pydantic-core==2.33.2
     # via pydantic
-pygments==2.19.1
+pygments==2.19.2
     # via rich
 pyodide-build==0.30.5
     # via -r .nox/update_constraints/tmp/constraints-pyodide.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index a174458ed..aaf8cb0cf 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.18-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.18-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.18-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.19-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.18-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.19-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.18-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.18-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.18-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.19-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.18-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.18-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.18-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.19-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.18-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.18-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.18-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.19-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.19-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.18-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.18-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.19-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.19-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.18-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.18-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.18-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.19-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.19-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.19-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.18-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.18-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.19-1
 
 [riscv64]
 
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 5bd523ae3..146ef39d8 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -71,8 +71,8 @@ title: Working examples
 | [mosec][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | A machine learning model serving framework powered by Rust |
 | [Picologging][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. |
 | [pybind11 cmake_example][]        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system |
-| [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
 | [markupsafe][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. |
+| [Rtree][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS |
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
@@ -181,8 +181,8 @@ title: Working examples
 [mosec]: https://github.com/mosecorg/mosec
 [Picologging]: https://github.com/microsoft/picologging
 [pybind11 cmake_example]: https://github.com/pybind/cmake_example
-[Rtree]: https://github.com/Toblerity/rtree
 [markupsafe]: https://github.com/pallets/markupsafe
+[Rtree]: https://github.com/Toblerity/rtree
 [KDEpy]: https://github.com/tommyod/KDEpy
 [dd-trace-py]: https://github.com/DataDog/dd-trace-py
 [tgcalls]: https://github.com/MarshalX/tgcalls

From 34b4f1e86e47792c683de9ef813ed4d614159846 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 24 Jun 2025 09:48:01 -0400
Subject: [PATCH 141/193] [pre-commit.ci] pre-commit autoupdate (#2474)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.0)
- [github.com/pre-commit/mirrors-mypy: v1.16.0 → v1.16.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.0...v1.16.1)
- [github.com/python-jsonschema/check-jsonschema: 0.33.0 → 0.33.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.0...0.33.1)

* chore: address new lints

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .pre-commit-config.yaml                   |  6 ++---
 bin/run_example_ci_configs.py             |  2 +-
 cibuildwheel/environment.py               |  9 +++----
 cibuildwheel/resources/install_certifi.py |  2 +-
 cibuildwheel/util/helpers.py              | 31 ++++++-----------------
 cibuildwheel/venv.py                      |  3 +--
 pyproject.toml                            |  1 +
 unit_test/build_selector_test.py          |  7 +++--
 8 files changed, 22 insertions(+), 39 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 94bf4a707..4f449ec03 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,14 +14,14 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.11.13
+  rev: v0.12.0
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
   - id: ruff-format
 
 - repo: https://github.com/pre-commit/mirrors-mypy
-  rev: v1.16.0
+  rev: v1.16.1
   hooks:
   - id: mypy
     name: mypy 3.11 on cibuildwheel/
@@ -80,7 +80,7 @@ repos:
 
 
 - repo: https://github.com/python-jsonschema/check-jsonschema
-  rev: 0.33.0
+  rev: 0.33.1
   hooks:
     - id: check-dependabot
     - id: check-github-actions
diff --git a/bin/run_example_ci_configs.py b/bin/run_example_ci_configs.py
index a6bd1bd06..6eb6a4f0c 100755
--- a/bin/run_example_ci_configs.py
+++ b/bin/run_example_ci_configs.py
@@ -28,7 +28,7 @@ def git_repo_has_changes() -> bool:
 
 def generate_basic_project(path: Path) -> None:
     sys.path.insert(0, "")
-    from test.test_projects.c import new_c_project
+    from test.test_projects.c import new_c_project  # noqa: PLC0415
 
     project = new_c_project()
     project.generate(path)
diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py
index a9a3f8627..4e603f99a 100644
--- a/cibuildwheel/environment.py
+++ b/cibuildwheel/environment.py
@@ -73,12 +73,16 @@ def evaluated_value(self, **_: Any) -> str:
         return self.value
 
 
+@dataclasses.dataclass
 class EnvironmentAssignmentBash:
     """
     An environment variable, in bash syntax. The value can use bash constructs
     like "$OTHER_VAR" and "$(command arg1 arg2)".
     """
 
+    name: str
+    value: str
+
     def __init__(self, assignment: str):
         name, equals, value = assignment.partition("=")
         if not equals:
@@ -96,11 +100,6 @@ def evaluated_value(
     def __repr__(self) -> str:
         return f"{self.name}={self.value}"
 
-    def __eq__(self, other: object) -> bool:
-        if isinstance(other, EnvironmentAssignmentBash):
-            return self.name == other.name and self.value == other.value
-        return False
-
 
 @dataclasses.dataclass(kw_only=True)
 class ParsedEnvironment:
diff --git a/cibuildwheel/resources/install_certifi.py b/cibuildwheel/resources/install_certifi.py
index 860521750..47f528cbc 100644
--- a/cibuildwheel/resources/install_certifi.py
+++ b/cibuildwheel/resources/install_certifi.py
@@ -33,7 +33,7 @@ def main() -> None:
         [sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"]
     )
 
-    import certifi
+    import certifi  # noqa: PLC0415
 
     # change working directory to the default SSL directory
     os.chdir(openssl_dir)
diff --git a/cibuildwheel/util/helpers.py b/cibuildwheel/util/helpers.py
index 363fd3345..f2e017ef9 100644
--- a/cibuildwheel/util/helpers.py
+++ b/cibuildwheel/util/helpers.py
@@ -1,3 +1,4 @@
+import dataclasses
 import itertools
 import os
 import re
@@ -5,7 +6,6 @@
 import textwrap
 from collections import defaultdict
 from collections.abc import Sequence
-from functools import total_ordering
 
 from ..typing import PathOrStr
 
@@ -140,19 +140,17 @@ def parse_key_value_string(
     return dict(result)
 
 
-@total_ordering
+@dataclasses.dataclass(order=True)
 class FlexibleVersion:
-    version_str: str
-    version_parts: tuple[int, ...]
-    suffix: str
-
-    def __init__(self, version_str: str) -> None:
-        self.version_str = version_str
+    version_parts: tuple[int, ...] = dataclasses.field(init=False, repr=False)
+    suffix: str = dataclasses.field(init=False, repr=False)
+    version_str: str = dataclasses.field(compare=False)
 
+    def __post_init__(self) -> None:
         # Split into numeric parts and the optional suffix
-        match = re.match(r"^[v]?(\d+(\.\d+)*)(.*)$", version_str)
+        match = re.match(r"^[v]?(\d+(\.\d+)*)(.*)$", self.version_str)
         if not match:
-            msg = f"Invalid version string: {version_str}"
+            msg = f"Invalid version string: {self.version_str}"
             raise ValueError(msg)
 
         version_part, _, suffix = match.groups()
@@ -172,18 +170,5 @@ def _remove_trailing_zeros(parts: tuple[int, ...]) -> tuple[int, ...]:
             parts = parts[:-1]
         return parts
 
-    def __eq__(self, other: object) -> bool:
-        if not isinstance(other, FlexibleVersion):
-            raise NotImplementedError()
-        return (self.version_parts, self.suffix) == (other.version_parts, other.suffix)
-
-    def __lt__(self, other: object) -> bool:
-        if not isinstance(other, FlexibleVersion):
-            raise NotImplementedError()
-        return (self.version_parts, self.suffix) < (other.version_parts, other.suffix)
-
-    def __repr__(self) -> str:
-        return f"FlexibleVersion('{self.version_str}')"
-
     def __str__(self) -> str:
         return self.version_str
diff --git a/cibuildwheel/venv.py b/cibuildwheel/venv.py
index 567ba4001..d004753c4 100644
--- a/cibuildwheel/venv.py
+++ b/cibuildwheel/venv.py
@@ -157,8 +157,7 @@ def virtualenv(
 def find_uv() -> Path | None:
     # Prefer uv in our environment
     with contextlib.suppress(ImportError, FileNotFoundError):
-        # pylint: disable-next=import-outside-toplevel
-        from uv import find_uv_bin
+        from uv import find_uv_bin  # noqa: PLC0415
 
         return Path(find_uv_bin())
 
diff --git a/pyproject.toml b/pyproject.toml
index 0b1ef884d..8b8aa27c4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -172,6 +172,7 @@ messages_control.disable = [
   "unsubscriptable-object",
   "wrong-import-position",
   "unused-argument",  # Handled by Ruff
+  "import-outside-toplevel",  # Handled by Ruff
   "broad-exception-raised",  # Could be improved eventually
   "consider-using-in",  # MyPy can't narrow "in"
 ]
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index 0b29b3887..601fe25f5 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -1,5 +1,6 @@
 from packaging.specifiers import SpecifierSet
 
+import cibuildwheel.selector
 from cibuildwheel.selector import BuildSelector, EnableGroup
 
 
@@ -224,10 +225,8 @@ def test_build_riscv64_enable():
 
 
 def test_testing_selector():
-    # local import to avoid pytest trying to collect this as a test class!
-    from cibuildwheel.selector import TestSelector
-
-    test_selector = TestSelector(skip_config="cp36-*")
+    # This is not a global import to keep pytest from collecting it as a test
+    test_selector = cibuildwheel.selector.TestSelector(skip_config="cp36-*")
 
     assert not test_selector("cp36-win_amd64")
     assert test_selector("cp37-manylinux_x86_64")

From 405ddd5315d4df8a9ffc569790a05e19f4344d7d Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sat, 28 Jun 2025 03:18:18 -0400
Subject: [PATCH 142/193] fix: pyodide missing some logging (#2477)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .github/workflows/test.yml        | 2 +-
 cibuildwheel/logger.py            | 4 ++--
 cibuildwheel/platforms/pyodide.py | 8 ++++++--
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index edc55f027..b99ee74d1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -34,7 +34,7 @@ concurrency:
 
 jobs:
   lint:
-    name: Linters (mypy, flake8, etc.)
+    name: Linters (mypy, ruff, etc.)
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v4
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index cee94d666..b2a9a3115 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -3,7 +3,7 @@
 import re
 import sys
 import time
-from typing import IO, AnyStr, Final
+from typing import IO, AnyStr, Final, Literal
 
 from .ci import CIProvider, detect_ci_provider
 
@@ -70,7 +70,7 @@ def __init__(self, *, unicode: bool) -> None:
 
 
 class Logger:
-    fold_mode: str
+    fold_mode: Literal["azure", "github", "travis", "disabled"]
     colors_enabled: bool
     unicode_enabled: bool
     active_build_identifier: str | None = None
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index 4e545260f..1ae5146b1 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -3,6 +3,7 @@
 import json
 import os
 import shutil
+import subprocess
 import sys
 import tomllib
 import typing
@@ -445,6 +446,7 @@ def build(options: Options, tmp_path: Path) -> None:
                         dest_dir=repaired_wheel_dir,
                     )
                     shell(repair_command_prepared, env=env)
+                    log.step_end()
                 else:
                     shutil.move(str(built_wheel), repaired_wheel_dir)
 
@@ -546,6 +548,8 @@ def build(options: Options, tmp_path: Path) -> None:
                         f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
                     )
                 built_wheels.append(output_wheel)
+            log.build_end()
 
-    finally:
-        pass
+    except subprocess.CalledProcessError as error:
+        msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
+        raise errors.FatalError(msg) from error

From 94fe0a212fa9c1bfabd54ee45473c28f30227c03 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 30 Jun 2025 10:31:30 -0400
Subject: [PATCH 143/193] [Bot] Update dependencies (#2482)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |   2 +-
 .../resources/constraints-pyodide313.txt      |   2 +-
 cibuildwheel/resources/nodejs.toml            |   4 +-
 .../resources/pinned_docker_images.cfg        |  54 +--
 .../python-build-standalone-releases.json     | 434 +++++++++---------
 docs/working-examples.md                      |   8 +-
 6 files changed, 252 insertions(+), 252 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index eca0ceee8..990988404 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -74,7 +74,7 @@ pyproject-hooks==1.2.0
     # via build
 requests==2.32.4
     # via pyodide-build
-resolvelib==1.1.0
+resolvelib==1.2.0
     # via pyodide-build
 rich==14.0.0
     # via
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index 32973e950..34832e66f 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -74,7 +74,7 @@ pyproject-hooks==1.2.0
     # via build
 requests==2.32.4
     # via pyodide-build
-resolvelib==1.1.0
+resolvelib==1.2.0
     # via pyodide-build
 rich==14.0.0
     # via
diff --git a/cibuildwheel/resources/nodejs.toml b/cibuildwheel/resources/nodejs.toml
index 2897eac28..01ca7aba9 100644
--- a/cibuildwheel/resources/nodejs.toml
+++ b/cibuildwheel/resources/nodejs.toml
@@ -1,3 +1,3 @@
 url = "https://nodejs.org/dist/"
-v22 = "v22.16.0"
-v20 = "v20.19.2"
+v22 = "v22.17.0"
+v20 = "v20.19.3"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index aaf8cb0cf..2dd897dfe 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,47 +1,47 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.28-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.28-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.28-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.28-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.19-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.28-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.28-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.28-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.19-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.19-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.19-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.28-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.28-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.19-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.28-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.28-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 73746ce4e..61a4bdc88 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,439 +1,439 @@
 {
   "releases": [
     {
-      "tag": "20250612",
+      "tag": "20250626",
       "assets": [
         {
-          "name": "cpython-3.10.18+20250612-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.10.18%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.11.13%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.12.11%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.13.5%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b2+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.14.0b2%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b3+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250612/cpython-3.9.23%2B20250612-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 146ef39d8..3207a54bd 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -38,8 +38,8 @@ title: Working examples
 | [River][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python |
 | [aiortc][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | WebRTC and ORTC implementation for Python using asyncio. |
 | [OpenSpiel][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. |
-| [UltraJSON][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings |
 | [Dependency Injector][]           | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Dependency injection framework for Python, uses Windows TravisCI |
+| [UltraJSON][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings |
 | [pyzmq][]                         | ![github icon][] ![circleci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython on CPython and CFFI on PyPy. ARM wheels for linux are built natively on CircleCI.  |
 | [CTranslate2][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. |
 | [Implicit][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels |
@@ -99,8 +99,8 @@ title: Working examples
 | [boost-histogram][]               | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
 | [Python-WebRTC][]                 | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
-| [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
 | [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
+| [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
 | [Arbor][]                         | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries.  |
 | [clang-format][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. |
 | [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
@@ -148,8 +148,8 @@ title: Working examples
 [River]: https://github.com/online-ml/river
 [aiortc]: https://github.com/aiortc/aiortc
 [OpenSpiel]: https://github.com/google-deepmind/open_spiel
-[UltraJSON]: https://github.com/ultrajson/ultrajson
 [Dependency Injector]: https://github.com/ets-labs/python-dependency-injector
+[UltraJSON]: https://github.com/ultrajson/ultrajson
 [pyzmq]: https://github.com/zeromq/pyzmq
 [CTranslate2]: https://github.com/OpenNMT/CTranslate2
 [Implicit]: https://github.com/benfred/implicit
@@ -209,8 +209,8 @@ title: Working examples
 [boost-histogram]: https://github.com/scikit-hep/boost-histogram
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
 [Python-WebRTC]: https://github.com/MarshalX/python-webrtc
-[fathon]: https://github.com/stfbnc/fathon
 [pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
+[fathon]: https://github.com/stfbnc/fathon
 [Arbor]: https://github.com/arbor-sim/arbor
 [clang-format]: https://github.com/ssciwr/clang-format-wheel
 [polaroid]: https://github.com/daggy1234/polaroid

From 920081014e8b136b4565824771dee1d489e046ef Mon Sep 17 00:00:00 2001
From: Andrew Murray <3112309+radarhere@users.noreply.github.com>
Date: Tue, 1 Jul 2025 00:32:25 +1000
Subject: [PATCH 144/193] feat: added Python 3.14 preview for iOS (#2475)

* Added Python 3.14 preview for iOS

* Test all Python versions with iOS

* add cp314-ios to the build selector table

---------

Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 cibuildwheel/resources/build-platforms.toml |  3 +++
 docs/options.md                             |  2 +-
 test/test_ios.py                            | 16 ++++------------
 3 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index dd8aef457..7a5a7628e 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -229,4 +229,7 @@ python_configurations = [
   { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
   { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
   { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
+  { identifier = "cp314-ios_arm64_iphoneos", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b5/Python-3.14-iOS-support.b5.tar.gz" },
+  { identifier = "cp314-ios_x86_64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b5/Python-3.14-iOS-support.b5.tar.gz" },
+  { identifier = "cp314-ios_arm64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b5/Python-3.14-iOS-support.b5.tar.gz" },
 ]
diff --git a/docs/options.md b/docs/options.md
index bd99997cc..6718c64fd 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -59,7 +59,7 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
 | Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                                                                   |                      |
 | Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                                                                   | cp312-pyodide_wasm32 |
 | Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 |
-| Python 3.14   | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 |                                                                                                   |                      |
+| Python 3.14   | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 | cp314-ios_arm64_iphoneos<br/>cp314-ios_arm64_iphonesimulator<br/>cp314-ios_x86_64_iphonesimulator |                      |
 | PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
 | PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
 | PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
diff --git a/test/test_ios.py b/test/test_ios.py
index b2d7e892a..f727db568 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -83,7 +83,6 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
         project_dir,
         add_env={
             "CIBW_BEFORE_BUILD": "does-exist",
-            "CIBW_BUILD": "cp313-*",
             "CIBW_XBUILD_TOOLS": "does-exist",
             "CIBW_TEST_SOURCES": "tests",
             "CIBW_TEST_COMMAND": "python -m this && python -m unittest discover tests test_platform.py",
@@ -94,7 +93,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd):
 
     # The expected wheels were produced.
     expected_wheels = utils.expected_wheels(
-        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313", "cp314-cp314"]
     )
     assert set(actual_wheels) == set(expected_wheels)
 
@@ -121,7 +120,6 @@ def test_no_test_sources(tmp_path, capfd):
             project_dir,
             add_env={
                 "CIBW_PLATFORM": "ios",
-                "CIBW_BUILD": "cp313-*",
                 "CIBW_TEST_REQUIRES": "pytest",
                 "CIBW_TEST_COMMAND": "python -m pytest",
                 "CIBW_XBUILD_TOOLS": "",
@@ -153,7 +151,6 @@ def test_ios_testing_with_placeholder(tmp_path, capfd):
             project_dir,
             add_env={
                 "CIBW_PLATFORM": "ios",
-                "CIBW_BUILD": "cp313-*",
                 "CIBW_TEST_REQUIRES": "pytest",
                 "CIBW_TEST_COMMAND": "pytest {project}/tests",
                 "CIBW_XBUILD_TOOLS": "",
@@ -180,7 +177,6 @@ def test_ios_test_command_short_circuit(tmp_path, capfd):
             project_dir,
             add_env={
                 "CIBW_PLATFORM": "ios",
-                "CIBW_BUILD": "cp313-*",
                 "CIBW_XBUILD_TOOLS": "",
                 "CIBW_TEST_SOURCES": "tests",
                 "CIBW_TEST_COMMAND": "python -m not_a_module && python -m this",
@@ -209,7 +205,6 @@ def test_missing_xbuild_tool(tmp_path, capfd):
             project_dir,
             add_env={
                 "CIBW_PLATFORM": "ios",
-                "CIBW_BUILD": "cp313-*",
                 "CIBW_TEST_COMMAND": "python -m tests",
                 "CIBW_XBUILD_TOOLS": "does-not-exist",
             },
@@ -235,7 +230,6 @@ def test_no_xbuild_tool_definition(tmp_path, capfd):
         project_dir,
         add_env={
             "CIBW_PLATFORM": "ios",
-            "CIBW_BUILD": "cp313-*",
             "CIBW_TEST_SKIP": "*",
         },
     )
@@ -245,7 +239,7 @@ def test_no_xbuild_tool_definition(tmp_path, capfd):
         "spam",
         "0.1.0",
         platform="ios",
-        python_abi_tags=["cp313-cp313"],
+        python_abi_tags=["cp313-cp313", "cp314-cp314"],
     )
     assert set(actual_wheels) == set(expected_wheels)
 
@@ -269,14 +263,13 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd):
         project_dir,
         add_env={
             "CIBW_PLATFORM": "ios",
-            "CIBW_BUILD": "cp313-*",
             "CIBW_TEST_SKIP": "*",
             "CIBW_XBUILD_TOOLS": "",
         },
     )
 
     expected_wheels = utils.expected_wheels(
-        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313", "cp314-cp314"]
     )
     assert set(actual_wheels) == set(expected_wheels)
 
@@ -306,7 +299,6 @@ def test_spam():
         project_dir,
         add_env={
             "CIBW_PLATFORM": "ios",
-            "CIBW_BUILD": "cp313-*",
             "CIBW_TEST_COMMAND": "pytest ./tests",
             "CIBW_TEST_SOURCES": "tests",
             "CIBW_TEST_REQUIRES": "pytest",
@@ -315,7 +307,7 @@ def test_spam():
     )
 
     expected_wheels = utils.expected_wheels(
-        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313", "cp314-cp314"]
     )
     assert set(actual_wheels) == set(expected_wheels)
 

From 40de3fe51083fa91bc8804c5a8de5c496f61ed52 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 1 Jul 2025 16:58:23 -0400
Subject: [PATCH 145/193] [pre-commit.ci] pre-commit autoupdate (#2483)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.0 → v0.12.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.0...v0.12.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4f449ec03..554869b01 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.12.0
+  rev: v0.12.1
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 95d2f3a92fbf80abe066b09418bbf128a8923df2 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Sat, 5 Jul 2025 19:03:42 +0100
Subject: [PATCH 146/193] Bump version: v3.0.1

---
 README.md                              | 20 +++++++++-----------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  7 +++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 41 insertions(+), 36 deletions(-)

diff --git a/README.md b/README.md
index 0abcf8f9e..e9c1068d1 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.0
+        run: python -m pip install cibuildwheel==3.0.1
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -227,6 +227,13 @@ Changelog
 
 <!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
 
+### v3.0.1
+
+_5 July 2025_
+
+- 🛠 Updates CPython 3.14 prerelease to 3.14.0b3 (#2471)
+- ✨ Adds a CPython 3.14 prerelease iOS build (only when prerelease builds are [enabled](https://cibuildwheel.pypa.io/en/stable/options/#enable)) (#2475)
+
 ### v3.0.0
 
 _11 June 2025_
@@ -285,16 +292,7 @@ _15 March 2025_
 - ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
 - 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
 
-### v2.23.0
-
-_1 March 2025_
-
-- ✨ Adds official support for the new GitHub Actions Arm runners. In fact these worked out-of-the-box, now we include them in our tests and example configs. (#2135 via #2281)
-- ✨ Adds support for building PyPy 3.11 wheels (#2268 via #2281)
-- 🛠 Adopts the beta pypa/manylinux image for armv7l builds (#2269 via #2281)
-- 🛠 Dependency updates, including Pyodide 0.27 (#2117 and #2281)
-
-<!-- [[[end]]] (sum: 9FCUvIZ+dy) -->
+<!-- [[[end]]] (sum: QKx9Hx5znR) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 528787cfc..055276878 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.0"
+__version__ = "3.0.1"
diff --git a/docs/changelog.md b/docs/changelog.md
index 06356a4f6..a6d61d49e 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,13 @@ title: Changelog
 
 # Changelog
 
+### v3.0.1
+
+_5 July 2025_
+
+- 🛠 Updates CPython 3.14 prerelease to 3.14.0b3 (#2471)
+- ✨ Adds a CPython 3.14 prerelease iOS build (only when prerelease builds are [enabled](https://cibuildwheel.pypa.io/en/stable/options/#enable)) (#2475)
+
 ### v3.0.0
 
 _11 June 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 25d9da73b..22db6178b 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -110,7 +110,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.0
+uses: pypa/cibuildwheel@v3.0.1
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -132,7 +132,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.0
+cibuildwheel==3.0.1
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -297,7 +297,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.0 --output-dir wheelhouse
+pipx run cibuildwheel==3.0.1 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index e73864f8e..aac1c0ef8 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.0
+        pip3 install cibuildwheel==3.0.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.0
+        python3 -m pip install cibuildwheel==3.0.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.0
+        pip install cibuildwheel==3.0.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 816a58135..d6c5d9e57 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0
+            python3 -m pip install --user cibuildwheel==3.0.1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.0
+            python3 -m pip install --user cibuildwheel==3.0.1
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.0
+            pip3 install cibuildwheel==3.0.1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index d59ecdad1..f1ff9a889 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0
+    - python -m pip install cibuildwheel==3.0.1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 0f552a15a..c25dadebf 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.0
+    - python -m pip install cibuildwheel==3.0.1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 596ac716c..8713fee90 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -42,7 +42,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0
+        uses: pypa/cibuildwheel@v3.0.1
         env:
           CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
         # Can also be configured directly, using `with:`
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 4bc1fd5b8..43ecce3eb 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0
+        uses: pypa/cibuildwheel@v3.0.1
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index 258b562e2..f7d769dfa 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.0
+        run: pipx run cibuildwheel==3.0.1
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 7b376bc15..2cace63ae 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.0
+        uses: pypa/cibuildwheel@v3.0.1
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 1a8e054a3..b730d1762 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.0
+    - python -m pip install cibuildwheel==3.0.1
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.0
+    - py -m pip install cibuildwheel==3.0.1
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.0
+    - python3 -m pip install cibuildwheel==3.0.1
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index db86bb583..8c96ed8d7 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.0
+    - python -m pip install cibuildwheel==3.0.1
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index fbf762793..86ea5bff8 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0
+  - python3 -m pip install cibuildwheel==3.0.1
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 796253f35..ab1c90884 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.0
+  - python3 -m pip install cibuildwheel==3.0.1
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index fc0dffa59..042f782f7 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.0 twine
+      install: python3 -m pip install cibuildwheel==3.0.1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.0 twine
+      install: python3 -m pip install cibuildwheel==3.0.1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 8b8aa27c4..b5665bc2e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.0"
+version = "3.0.1"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From a6ec930f15689c27998ef75440c06258ae143a33 Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Tue, 8 Jul 2025 09:14:34 +0100
Subject: [PATCH 147/193] Welcome to our new platform maintainers! (#2481)

* Add new platform maintainers to README!

* Add a codeowners file
---
 .github/CODEOWNERS |  2 ++
 README.md          | 10 ++++++++++
 2 files changed, 12 insertions(+)
 create mode 100644 .github/CODEOWNERS

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..fb4409e64
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,2 @@
+cibuildwheel/platforms/ios.py         @freakboy3742
+cibuildwheel/platforms/pyodide.py     @hoodmane @ryanking13 @agriyakhetarpal
diff --git a/README.md b/README.md
index e9c1068d1..8c69f9ae0 100644
--- a/README.md
+++ b/README.md
@@ -312,12 +312,22 @@ Everyone interacting with the cibuildwheel project via codebase, issue tracker,
 Maintainers
 -----------
 
+Core:
+
 - Joe Rickerby [@joerick](https://github.com/joerick)
 - Yannick Jadoul [@YannickJadoul](https://github.com/YannickJadoul)
 - Matthieu Darbois [@mayeut](https://github.com/mayeut)
 - Henry Schreiner [@henryiii](https://github.com/henryiii)
 - Grzegorz Bokota [@Czaki](https://github.com/Czaki)
 
+Platform maintainers:
+
+- Russell Keith-Magee [@freakboy3742](https://github.com/freakboy3742) (iOS)
+- Agriya Khetarpal [@agriyakhetarpal](https://github.com/agriyakhetarpal) (Pyodide)
+- Hood Chatham [@hoodmane](https://github.com/hoodmane) (Pyodide)
+- Gyeongjae Choi [@ryanking13](https://github.com/ryanking13) (Pyodide)
+- Tim Felgentreff [@timfel](https://github.com/timfel) (GraalPy)
+
 Credits
 -------
 

From dafc3545eb752633f572b1d93a71684619804cc5 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 8 Jul 2025 04:32:22 -0400
Subject: [PATCH 148/193] fix: support only one output wheel from repair
 (#2478)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/errors.py          | 18 +++++++++++++++++
 cibuildwheel/platforms/linux.py | 34 ++++++++++++++-------------------
 2 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/cibuildwheel/errors.py b/cibuildwheel/errors.py
index 28939719f..9f0a71f15 100644
--- a/cibuildwheel/errors.py
+++ b/cibuildwheel/errors.py
@@ -85,3 +85,21 @@ def __init__(self) -> None:
         )
         super().__init__(message)
         self.return_code = 8
+
+
+class RepairStepProducedMultipleWheelsError(FatalError):
+    def __init__(self, wheels: list[str]) -> None:
+        message = textwrap.dedent(
+            f"""
+            Build failed because the repair step completed successfully but
+            produced multiple wheels: {wheels}
+
+            Your `repair-wheel-command` is expected to place one repaired
+            wheel in the {{dest_dir}} directory. See the documentation for
+            example configurations:
+
+            https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command
+            """
+        )
+        super().__init__(message)
+        self.return_code = 8
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 82a695e69..2c063a2d5 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -253,7 +253,7 @@ def build_in_container(
             print(
                 f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
             )
-            repaired_wheels = [compatible_wheel]
+            repaired_wheel = compatible_wheel
         else:
             if build_options.before_build:
                 log.step("Running before_build...")
@@ -325,14 +325,16 @@ def build_in_container(
             else:
                 container.call(["mv", built_wheel, repaired_wheel_dir])
 
-            repaired_wheels = container.glob(repaired_wheel_dir, "*.whl")
+            match container.glob(repaired_wheel_dir, "*.whl"):
+                case []:
+                    raise errors.RepairStepProducedNoWheelError()
+                case [repaired_wheel]:
+                    pass
+                case too_many:
+                    raise errors.RepairStepProducedMultipleWheelsError([p.name for p in too_many])
 
-            if not repaired_wheels:
-                raise errors.RepairStepProducedNoWheelError()
-
-            for repaired_wheel in repaired_wheels:
-                if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
-                    raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
+            if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
+                raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
 
         if build_options.test_command and build_options.test_selector(config.identifier):
             log.step("Testing wheel...")
@@ -374,14 +376,8 @@ def build_in_container(
                 container.call(["sh", "-c", before_test_prepared], env=virtualenv_env)
 
             # Install the wheel we just built
-            # Note: If auditwheel produced two wheels, it's because the earlier produced wheel
-            # conforms to multiple manylinux standards. These multiple versions of the wheel are
-            # functionally the same, differing only in name, wheel metadata, and possibly include
-            # different external shared libraries. so it doesn't matter which one we run the tests on.
-            # Let's just pick the first one.
-            wheel_to_test = repaired_wheels[0]
             container.call(
-                [*pip, "install", str(wheel_to_test) + build_options.test_extras],
+                [*pip, "install", str(repaired_wheel) + build_options.test_extras],
                 env=virtualenv_env,
             )
 
@@ -394,7 +390,7 @@ def build_in_container(
                 build_options.test_command,
                 project=container_project_path,
                 package=container_package_dir,
-                wheel=wheel_to_test,
+                wheel=repaired_wheel,
             )
 
             test_cwd = testing_temp_dir / "test_cwd"
@@ -420,10 +416,8 @@ def build_in_container(
         # move repaired wheels to output
         if compatible_wheel is None:
             container.call(["mkdir", "-p", container_output_dir])
-            container.call(["mv", *repaired_wheels, container_output_dir])
-            built_wheels.extend(
-                container_output_dir / repaired_wheel.name for repaired_wheel in repaired_wheels
-            )
+            container.call(["mv", repaired_wheel, container_output_dir])
+            built_wheels.append(container_output_dir / repaired_wheel.name)
 
         log.build_end()
 

From 46ef6044e2f0854af96d8fcd1b45beb65942b1ca Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Tue, 8 Jul 2025 10:32:51 +0200
Subject: [PATCH 149/193] feat: add support for manylinux_2_28 i686 image
 (#2480)

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
---
 bin/update_docker.py                           | 12 +++++++++++-
 cibuildwheel/resources/defaults.toml           |  4 ++--
 .../resources/pinned_docker_images.cfg         |  2 ++
 docs/options.md                                |  6 +++---
 test/test_manylinuxXXXX_only.py                | 10 +++++-----
 test/utils.py                                  |  2 +-
 unit_test/main_tests/main_options_test.py      |  3 ++-
 unit_test/option_prepare_test.py               | 18 +++++++++++++-----
 unit_test/options_toml_test.py                 |  4 ++--
 9 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/bin/update_docker.py b/bin/update_docker.py
index 3c6ef79ed..20573a7fc 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -43,7 +43,17 @@ def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None
     ),
     # manylinux_2_28 images
     PyPAImage(
-        "manylinux_2_28", ["x86_64", "aarch64", "ppc64le", "s390x", "pypy_x86_64", "pypy_aarch64"]
+        "manylinux_2_28",
+        [
+            "x86_64",
+            "i686",
+            "aarch64",
+            "ppc64le",
+            "s390x",
+            "pypy_x86_64",
+            "pypy_i686",
+            "pypy_aarch64",
+        ],
     ),
     # manylinux_2_31 images
     PyPAImage("manylinux_2_31", ["armv7l"]),
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 396c1d3cc..6a57f2d03 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -31,14 +31,14 @@ container-engine = "docker"
 pyodide-version = ""
 
 manylinux-x86_64-image = "manylinux_2_28"
-manylinux-i686-image = "manylinux2014"
+manylinux-i686-image = "manylinux_2_28"
 manylinux-aarch64-image = "manylinux_2_28"
 manylinux-ppc64le-image = "manylinux_2_28"
 manylinux-s390x-image = "manylinux_2_28"
 manylinux-armv7l-image = "manylinux_2_31"
 manylinux-riscv64-image = "ghcr.io/pypa/cibuildwheel/no_default_image:please_use_override"
 manylinux-pypy_x86_64-image = "manylinux_2_28"
-manylinux-pypy_i686-image = "manylinux2014"
+manylinux-pypy_i686-image = "manylinux_2_28"
 manylinux-pypy_aarch64-image = "manylinux_2_28"
 
 musllinux-x86_64-image = "musllinux_1_2"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 2dd897dfe..a6b550730 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -6,6 +6,7 @@ musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.28-1
 
 [i686]
 manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.06.28-1
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.28-1
 
 [aarch64]
@@ -33,6 +34,7 @@ manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.28-1
 
 [pypy_i686]
 manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.28-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.06.28-1
 
 [pypy_aarch64]
 manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.28-1
diff --git a/docs/options.md b/docs/options.md
index 6718c64fd..ed2fadcba 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1009,7 +1009,7 @@ The available options are:
 | Option                         | Default                                                         |
 |--------------------------------|-----------------------------------------------------------------|
 | `manylinux_x86_64-image`       | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
-| `manylinux-i686-image`         | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
+| `manylinux-i686-image`         | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_i686)    |
 | `manylinux-pypy_x86_64-image`  | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64)  |
 | `manylinux-aarch64-image`      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
 | `manylinux-ppc64le-image`      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
@@ -1017,7 +1017,7 @@ The available options are:
 | `manylinux-armv7l-image`       | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l)  |
 | `manylinux-riscv64-image`      | No default                                                      |
 | `manylinux-pypy_aarch64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
-| `manylinux-pypy_i686-image`    | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686)      |
+| `manylinux-pypy_i686-image`    | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_i686)    |
 | `musllinux_x86_64-image`       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64)    |
 | `musllinux-i686-image`         | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686)      |
 | `musllinux-aarch64-image`      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64)   |
@@ -1029,7 +1029,7 @@ The available options are:
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
 
 For `manylinux-*-image`, except `manylinux-armv7l-image`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
-`manylinux_2_28` and `manylinux_2_34` are not supported for `i686` architecture.
+`manylinux_2_34` is not supported for `i686` architecture.
 
 For `manylinux-armv7l-image`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
 
diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py
index 483d40f99..8754b363b 100644
--- a/test/test_manylinuxXXXX_only.py
+++ b/test/test_manylinuxXXXX_only.py
@@ -64,7 +64,7 @@
 def test(manylinux_image, tmp_path):
     if utils.get_platform() != "linux":
         pytest.skip("the container image test is only relevant to the linux build")
-    elif manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "i686":
+    elif manylinux_image == "manylinux_2_34" and platform.machine() == "i686":
         pytest.skip(f"{manylinux_image} doesn't exist for i686 architecture")
 
     project_dir = tmp_path / "project"
@@ -85,8 +85,8 @@ def test(manylinux_image, tmp_path):
         "CIBW_MANYLINUX_PYPY_AARCH64_IMAGE": manylinux_image,
         "CIBW_MANYLINUX_PYPY_I686_IMAGE": manylinux_image,
     }
-    if manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "x86_64":
-        # We don't have a manylinux_2_28+ image for i686
+    if manylinux_image == "manylinux_2_34" and platform.machine() == "x86_64":
+        # We don't have a manylinux_2_34+ image for i686
         add_env["CIBW_ARCHS"] = "x86_64"
     if platform.machine() == "aarch64":
         # We just have a manylinux_2_31 image for armv7l
@@ -104,8 +104,8 @@ def test(manylinux_image, tmp_path):
         musllinux_versions=[],
     )
 
-    if manylinux_image in {"manylinux_2_28", "manylinux_2_34"} and platform.machine() == "x86_64":
-        # We don't have a manylinux_2_28+ image for i686
+    if manylinux_image == "manylinux_2_34" and platform.machine() == "x86_64":
+        # We don't have a manylinux_2_34+ image for i686
         expected_wheels = [w for w in expected_wheels if "i686" not in w]
 
     if platform.machine() == "aarch64":
diff --git a/test/utils.py b/test/utils.py
index 782934bdf..749e54dc1 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -252,7 +252,7 @@ def _expected_wheels(
     if manylinux_versions is None:
         manylinux_versions = {
             "armv7l": ["manylinux2014", "manylinux_2_17", "manylinux_2_31"],
-            "i686": ["manylinux1", "manylinux2014", "manylinux_2_17", "manylinux_2_5"],
+            "i686": ["manylinux1", "manylinux_2_28", "manylinux_2_5"],
             "x86_64": ["manylinux1", "manylinux_2_28", "manylinux_2_5"],
             "riscv64": ["manylinux_2_31", "manylinux_2_35"],
         }.get(machine_arch, ["manylinux2014", "manylinux_2_17", "manylinux_2_28"])
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index 731d10adc..cf421ce49 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -111,8 +111,9 @@ def test_empty_selector(monkeypatch):
         ("x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
         ("x86_64", "custom_image", "custom_image"),
-        ("i686", None, "quay.io/pypa/manylinux2014_i686:*"),
+        ("i686", None, "quay.io/pypa/manylinux_2_28_i686:*"),
         ("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"),
+        ("i686", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_i686:*"),
         ("i686", "custom_image", "custom_image"),
         ("pypy_x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
         ("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index d2a1a0826..945cb3b0a 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -70,7 +70,7 @@ def test_build_default_launches(monkeypatch):
     assert identifiers == {f"{x}-manylinux_x86_64" for x in DEFAULT_IDS}
 
     kwargs = build_in_container.call_args_list[1][1]
-    assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"]
+    assert "quay.io/pypa/manylinux_2_28_i686" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
 
@@ -128,7 +128,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
 
     build_in_container = typing.cast(mock.Mock, platforms.linux.build_in_container)
 
-    assert build_in_container.call_count == 6
+    assert build_in_container.call_count == 7
 
     kwargs = build_in_container.call_args_list[0][1]
     assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
@@ -189,11 +189,19 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
     assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
-
     identifiers = {x.identifier for x in kwargs["platform_configs"]}
-    assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS if "gp" not in x}
+    assert identifiers == {"cp38-manylinux_i686", "cp39-manylinux_i686"}
 
     kwargs = build_in_container.call_args_list[4][1]
+    assert "quay.io/pypa/manylinux_2_28_i686" in kwargs["container"]["image"]
+    assert kwargs["container"]["cwd"] == PurePosixPath("/project")
+    assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
+    identifiers = {x.identifier for x in kwargs["platform_configs"]}
+    assert identifiers == {
+        f"{x}-manylinux_i686" for x in ALL_IDS - {"cp38", "cp39"} if "gp" not in x
+    }
+
+    kwargs = build_in_container.call_args_list[5][1]
     assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
@@ -202,7 +210,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
         f"{x}-musllinux_x86_64" for x in ALL_IDS if "pp" not in x and "gp" not in x
     }
 
-    kwargs = build_in_container.call_args_list[5][1]
+    kwargs = build_in_container.call_args_list[6][1]
     assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"]
     assert kwargs["container"]["cwd"] == PurePosixPath("/project")
     assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
diff --git a/unit_test/options_toml_test.py b/unit_test/options_toml_test.py
index 6ec634152..f256fff60 100644
--- a/unit_test/options_toml_test.py
+++ b/unit_test/options_toml_test.py
@@ -69,7 +69,7 @@ def test_simple_settings(tmp_path, platform, fname):
     assert options_reader.get("test-groups", option_format=ListFormat(" ")) == "three four"
 
     assert options_reader.get("manylinux-x86_64-image") == "manylinux_2_28"
-    assert options_reader.get("manylinux-i686-image") == "manylinux2014"
+    assert options_reader.get("manylinux-i686-image") == "manylinux_2_28"
 
     with pytest.raises(OptionsReaderError):
         # fails because the option is a table and the option_format only works with lists
@@ -104,7 +104,7 @@ def test_envvar_override(tmp_path, platform):
 
     assert options_reader.get("build") == "cp38*"
     assert options_reader.get("manylinux-x86_64-image") == "manylinux_2_24"
-    assert options_reader.get("manylinux-i686-image") == "manylinux2014"
+    assert options_reader.get("manylinux-i686-image") == "manylinux_2_28"
 
     assert (
         options_reader.get("xbuild-tools", option_format=ListFormat(" ", quote=shlex.quote))

From 834ea4051085bdb957d8e0c87944ce9b00e8e658 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 8 Jul 2025 08:59:20 -0400
Subject: [PATCH 150/193] chore: pattern matching (#2434)

* chore: use match for stringify_setting

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: more match statements

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 cibuildwheel/oci_container.py     |  73 +++++++++--------
 cibuildwheel/options.py           |  62 +++++++-------
 cibuildwheel/platforms/ios.py     |  86 ++++++++++----------
 cibuildwheel/platforms/linux.py   |  63 ++++++++-------
 cibuildwheel/platforms/macos.py   | 130 ++++++++++++++++--------------
 cibuildwheel/platforms/windows.py | 106 ++++++++++++------------
 cibuildwheel/projectfiles.py      |  46 ++++-------
 7 files changed, 286 insertions(+), 280 deletions(-)

diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py
index 59a1ca7e2..29ddb9904 100644
--- a/cibuildwheel/oci_container.py
+++ b/cibuildwheel/oci_container.py
@@ -95,43 +95,46 @@ def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
     try:
         version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True)
         version_info = json.loads(version_string.strip())
-        if engine.name == "docker":
-            client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"])
-            server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"])
-            # --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag
-            version = min(client_api_version, server_api_version)
-            minimum_version = FlexibleVersion("1.41")
-            minimum_version_str = "20.10.0"  # docker version
-            error_msg = textwrap.dedent(
-                f"""
-                Build failed because {engine.name} is too old.
-
-                cibuildwheel requires {engine.name}>={minimum_version_str} running API version {minimum_version}.
-                The API version found by cibuildwheel is {version}.
-                """
-            )
-        elif engine.name == "podman":
-            # podman uses the same version string for "Version" & "ApiVersion"
-            client_version = FlexibleVersion(version_info["Client"]["Version"])
-            if "Server" in version_info:
-                server_version = FlexibleVersion(version_info["Server"]["Version"])
-            else:
-                server_version = client_version
-            # --platform support was introduced in v3
-            version = min(client_version, server_version)
-            minimum_version = FlexibleVersion("3")
-            error_msg = textwrap.dedent(
-                f"""
-                Build failed because {engine.name} is too old.
-
-                cibuildwheel requires {engine.name}>={minimum_version}.
-                The version found by cibuildwheel is {version}.
-                """
-            )
-        else:
-            assert_never(engine.name)
+        match engine.name:
+            case "docker":
+                client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"])
+                server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"])
+                # --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag
+                version = min(client_api_version, server_api_version)
+                minimum_version = FlexibleVersion("1.41")
+                minimum_version_str = "20.10.0"  # docker version
+                error_msg = textwrap.dedent(
+                    f"""
+                    Build failed because {engine.name} is too old.
+
+                    cibuildwheel requires {engine.name}>={minimum_version_str} running API version {minimum_version}.
+                    The API version found by cibuildwheel is {version}.
+                    """
+                )
+            case "podman":
+                # podman uses the same version string for "Version" & "ApiVersion"
+                client_version = FlexibleVersion(version_info["Client"]["Version"])
+                if "Server" in version_info:
+                    server_version = FlexibleVersion(version_info["Server"]["Version"])
+                else:
+                    server_version = client_version
+                # --platform support was introduced in v3
+                version = min(client_version, server_version)
+                minimum_version = FlexibleVersion("3")
+                error_msg = textwrap.dedent(
+                    f"""
+                    Build failed because {engine.name} is too old.
+
+                    cibuildwheel requires {engine.name}>={minimum_version}.
+                    The version found by cibuildwheel is {version}.
+                    """
+                )
+            case _:
+                assert_never(engine.name)
+
         if version < minimum_version:
             raise OCIEngineTooOldError(error_msg) from None
+
     except (subprocess.CalledProcessError, KeyError, ValueError) as e:
         msg = f"Build failed because {engine.name} is too old or is not working properly."
         raise OCIEngineTooOldError(msg) from e
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 7c98f1e21..e88998b29 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -343,40 +343,46 @@ def _apply_inherit_rule(
         msg = f"Don't know how to merge {before!r} and {after!r} with {rule}"
         raise OptionsReaderError(msg)
 
-    if rule == InheritRule.APPEND:
-        return option_format.merge_values(before, after)
-    if rule == InheritRule.PREPEND:
-        return option_format.merge_values(after, before)
-
-    assert_never(rule)
+    match rule:
+        case InheritRule.APPEND:
+            return option_format.merge_values(before, after)
+        case InheritRule.PREPEND:
+            return option_format.merge_values(after, before)
+        case _:
+            assert_never(rule)
 
 
 def _stringify_setting(
     setting: SettingValue,
     option_format: OptionFormat | None,
 ) -> str:
-    if isinstance(setting, Mapping):
-        try:
-            if option_format is None:
-                raise OptionFormat.NotSupported
-            return option_format.format_table(setting)
-        except OptionFormat.NotSupported:
-            msg = f"Error converting {setting!r} to a string: this setting doesn't accept a table"
-            raise OptionsReaderError(msg) from None
-
-    if not isinstance(setting, str) and isinstance(setting, Sequence):
-        try:
-            if option_format is None:
-                raise OptionFormat.NotSupported
-            return option_format.format_list(setting)
-        except OptionFormat.NotSupported:
-            msg = f"Error converting {setting!r} to a string: this setting doesn't accept a list"
-            raise OptionsReaderError(msg) from None
-
-    if isinstance(setting, bool | int):
-        return str(setting)
-
-    return setting
+    match setting:
+        case {}:
+            assert isinstance(setting, Mapping)  # MyPy 1.15 doesn't narrow this for us
+            try:
+                if option_format is None:
+                    raise OptionFormat.NotSupported
+                return option_format.format_table(setting)
+            except OptionFormat.NotSupported:
+                msg = (
+                    f"Error converting {setting!r} to a string: this setting doesn't accept a table"
+                )
+                raise OptionsReaderError(msg) from None
+        case bool() | int():
+            return str(setting)
+        case [*_]:
+            try:
+                if option_format is None:
+                    raise OptionFormat.NotSupported
+                return option_format.format_list(setting)
+            except OptionFormat.NotSupported:
+                msg = (
+                    f"Error converting {setting!r} to a string: this setting doesn't accept a list"
+                )
+                raise OptionsReaderError(msg) from None
+        case _:
+            assert isinstance(setting, str)  # MyPy 1.15 doesn't narrow this for us
+            return setting
 
 
 class OptionsReader:
diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index 1a3d2d274..b0511a2a4 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -387,20 +387,21 @@ def setup_python(
     env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
 
     log.step("Installing build tools...")
-    if build_frontend == "pip":
-        # No additional build tools required
-        pass
-    elif build_frontend == "build":
-        call(
-            "pip",
-            "install",
-            "--upgrade",
-            "build[virtualenv]",
-            *constraint_flags(dependency_constraint),
-            env=env,
-        )
-    else:
-        assert_never(build_frontend)
+    match build_frontend:
+        case "pip":
+            # No additional build tools required
+            pass
+        case "build":
+            call(
+                "pip",
+                "install",
+                "--upgrade",
+                "build[virtualenv]",
+                *constraint_flags(dependency_constraint),
+                env=env,
+            )
+        case _:
+            assert_never(build_frontend)
 
     return target_install_path, env
 
@@ -494,34 +495,35 @@ def build(options: Options, tmp_path: Path) -> None:
                 if constraints_path:
                     combine_constraints(build_env, constraints_path, None)
 
-                if build_frontend.name == "pip":
-                    # Path.resolve() is needed. Without it pip wheel may try to
-                    # fetch package from pypi.org. See
-                    # https://github.com/pypa/cibuildwheel/pull/369
-                    call(
-                        "python",
-                        "-m",
-                        "pip",
-                        "wheel",
-                        build_options.package_dir.resolve(),
-                        f"--wheel-dir={built_wheel_dir}",
-                        "--no-deps",
-                        *extra_flags,
-                        env=build_env,
-                    )
-                elif build_frontend.name == "build":
-                    call(
-                        "python",
-                        "-m",
-                        "build",
-                        build_options.package_dir,
-                        "--wheel",
-                        f"--outdir={built_wheel_dir}",
-                        *extra_flags,
-                        env=build_env,
-                    )
-                else:
-                    assert_never(build_frontend)
+                match build_frontend.name:
+                    case "pip":
+                        # Path.resolve() is needed. Without it pip wheel may try to
+                        # fetch package from pypi.org. See
+                        # https://github.com/pypa/cibuildwheel/pull/369
+                        call(
+                            "python",
+                            "-m",
+                            "pip",
+                            "wheel",
+                            build_options.package_dir.resolve(),
+                            f"--wheel-dir={built_wheel_dir}",
+                            "--no-deps",
+                            *extra_flags,
+                            env=build_env,
+                        )
+                    case "build":
+                        call(
+                            "python",
+                            "-m",
+                            "build",
+                            build_options.package_dir,
+                            "--wheel",
+                            f"--outdir={built_wheel_dir}",
+                            *extra_flags,
+                            env=build_env,
+                        )
+                    case _:
+                        assert_never(build_frontend)
 
                 test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl"))
 
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 2c063a2d5..1a388d95f 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -275,37 +275,38 @@ def build_in_container(
                 build_frontend, build_options.build_verbosity, build_options.config_settings
             )
 
-            if build_frontend.name == "pip":
-                container.call(
-                    [
-                        "python",
-                        "-m",
-                        "pip",
-                        "wheel",
-                        container_package_dir,
-                        f"--wheel-dir={built_wheel_dir}",
-                        "--no-deps",
-                        *extra_flags,
-                    ],
-                    env=env,
-                )
-            elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
-                if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
-                    extra_flags += ["--installer=uv"]
-                container.call(
-                    [
-                        "python",
-                        "-m",
-                        "build",
-                        container_package_dir,
-                        "--wheel",
-                        f"--outdir={built_wheel_dir}",
-                        *extra_flags,
-                    ],
-                    env=env,
-                )
-            else:
-                assert_never(build_frontend)
+            match build_frontend.name:
+                case "pip":
+                    container.call(
+                        [
+                            "python",
+                            "-m",
+                            "pip",
+                            "wheel",
+                            container_package_dir,
+                            f"--wheel-dir={built_wheel_dir}",
+                            "--no-deps",
+                            *extra_flags,
+                        ],
+                        env=env,
+                    )
+                case "build" | "build[uv]":
+                    if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
+                        extra_flags += ["--installer=uv"]
+                    container.call(
+                        [
+                            "python",
+                            "-m",
+                            "build",
+                            container_package_dir,
+                            "--wheel",
+                            f"--outdir={built_wheel_dir}",
+                            *extra_flags,
+                        ],
+                        env=env,
+                    )
+                case _:
+                    assert_never(build_frontend)
 
             built_wheel = container.glob(built_wheel_dir, "*.whl")[0]
 
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index 99ca915b8..cc0a5a8ef 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -345,39 +345,40 @@ def setup_python(
             env.setdefault("SDKROOT", arm64_compatible_sdks[0])
 
     log.step("Installing build tools...")
-    if build_frontend == "pip":
-        call(
-            "pip",
-            "install",
-            "--upgrade",
-            "delocate",
-            *constraint_flags(dependency_constraint),
-            env=env,
-        )
-    elif build_frontend == "build":
-        call(
-            "pip",
-            "install",
-            "--upgrade",
-            "delocate",
-            "build[virtualenv]",
-            *constraint_flags(dependency_constraint),
-            env=env,
-        )
-    elif build_frontend == "build[uv]":
-        assert uv_path is not None
-        call(
-            uv_path,
-            "pip",
-            "install",
-            "--upgrade",
-            "delocate",
-            "build[virtualenv, uv]",
-            *constraint_flags(dependency_constraint),
-            env=env,
-        )
-    else:
-        assert_never(build_frontend)
+    match build_frontend:
+        case "pip":
+            call(
+                "pip",
+                "install",
+                "--upgrade",
+                "delocate",
+                *constraint_flags(dependency_constraint),
+                env=env,
+            )
+        case "build":
+            call(
+                "pip",
+                "install",
+                "--upgrade",
+                "delocate",
+                "build[virtualenv]",
+                *constraint_flags(dependency_constraint),
+                env=env,
+            )
+        case "build[uv]":
+            assert uv_path is not None
+            call(
+                uv_path,
+                "pip",
+                "install",
+                "--upgrade",
+                "delocate",
+                "build[virtualenv, uv]",
+                *constraint_flags(dependency_constraint),
+                env=env,
+            )
+        case _:
+            assert_never(build_frontend)
 
     return base_python, env
 
@@ -467,35 +468,40 @@ def build(options: Options, tmp_path: Path) -> None:
                         build_env, constraints_path, identifier_tmp_dir if use_uv else None
                     )
 
-                if build_frontend.name == "pip":
-                    # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
-                    # see https://github.com/pypa/cibuildwheel/pull/369
-                    call(
-                        "python",
-                        "-m",
-                        "pip",
-                        "wheel",
-                        build_options.package_dir.resolve(),
-                        f"--wheel-dir={built_wheel_dir}",
-                        "--no-deps",
-                        *extra_flags,
-                        env=build_env,
-                    )
-                elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
-                    if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
-                        extra_flags.append("--installer=uv")
-                    call(
-                        "python",
-                        "-m",
-                        "build",
-                        build_options.package_dir,
-                        "--wheel",
-                        f"--outdir={built_wheel_dir}",
-                        *extra_flags,
-                        env=build_env,
-                    )
-                else:
-                    assert_never(build_frontend)
+                match build_frontend.name:
+                    case "pip":
+                        # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
+                        # see https://github.com/pypa/cibuildwheel/pull/369
+                        call(
+                            "python",
+                            "-m",
+                            "pip",
+                            "wheel",
+                            build_options.package_dir.resolve(),
+                            f"--wheel-dir={built_wheel_dir}",
+                            "--no-deps",
+                            *extra_flags,
+                            env=build_env,
+                        )
+                    case "build" | "build[uv]":
+                        if (
+                            use_uv
+                            and "--no-isolation" not in extra_flags
+                            and "-n" not in extra_flags
+                        ):
+                            extra_flags.append("--installer=uv")
+                        call(
+                            "python",
+                            "-m",
+                            "build",
+                            build_options.package_dir,
+                            "--wheel",
+                            f"--outdir={built_wheel_dir}",
+                            *extra_flags,
+                            env=build_env,
+                        )
+                    case _:
+                        assert_never(build_frontend)
 
                 built_wheel = next(built_wheel_dir.glob("*.whl"))
 
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index c57a53de0..6913092cd 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -305,26 +305,27 @@ def setup_python(
         call("pip", "--version", env=env)
 
     log.step("Installing build tools...")
-    if build_frontend == "build":
-        call(
-            "pip",
-            "install",
-            "--upgrade",
-            "build[virtualenv]",
-            *constraint_flags(dependency_constraint),
-            env=env,
-        )
-    elif build_frontend == "build[uv]":
-        assert uv_path is not None
-        call(
-            uv_path,
-            "pip",
-            "install",
-            "--upgrade",
-            "build[virtualenv]",
-            *constraint_flags(dependency_constraint),
-            env=env,
-        )
+    match build_frontend:
+        case "build":
+            call(
+                "pip",
+                "install",
+                "--upgrade",
+                "build[virtualenv]",
+                *constraint_flags(dependency_constraint),
+                env=env,
+            )
+        case "build[uv]":
+            assert uv_path is not None
+            call(
+                uv_path,
+                "pip",
+                "install",
+                "--upgrade",
+                "build[virtualenv]",
+                *constraint_flags(dependency_constraint),
+                env=env,
+            )
 
     if python_libs_base:
         # Set up the environment for various backends to enable cross-compilation
@@ -467,36 +468,41 @@ def build(options: Options, tmp_path: Path) -> None:
                 if constraints_path:
                     combine_constraints(build_env, constraints_path, identifier_tmp_dir)
 
-                if build_frontend.name == "pip":
-                    # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
-                    # see https://github.com/pypa/cibuildwheel/pull/369
-                    call(
-                        "python",
-                        "-m",
-                        "pip",
-                        "wheel",
-                        options.globals.package_dir.resolve(),
-                        f"--wheel-dir={built_wheel_dir}",
-                        "--no-deps",
-                        *extra_flags,
-                        env=build_env,
-                    )
-                elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
-                    if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
-                        extra_flags.append("--installer=uv")
-
-                    call(
-                        "python",
-                        "-m",
-                        "build",
-                        build_options.package_dir,
-                        "--wheel",
-                        f"--outdir={built_wheel_dir}",
-                        *extra_flags,
-                        env=build_env,
-                    )
-                else:
-                    assert_never(build_frontend)
+                match build_frontend.name:
+                    case "pip":
+                        # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
+                        # see https://github.com/pypa/cibuildwheel/pull/369
+                        call(
+                            "python",
+                            "-m",
+                            "pip",
+                            "wheel",
+                            options.globals.package_dir.resolve(),
+                            f"--wheel-dir={built_wheel_dir}",
+                            "--no-deps",
+                            *extra_flags,
+                            env=build_env,
+                        )
+                    case "build" | "build[uv]":
+                        if (
+                            use_uv
+                            and "--no-isolation" not in extra_flags
+                            and "-n" not in extra_flags
+                        ):
+                            extra_flags.append("--installer=uv")
+
+                        call(
+                            "python",
+                            "-m",
+                            "build",
+                            build_options.package_dir,
+                            "--wheel",
+                            f"--outdir={built_wheel_dir}",
+                            *extra_flags,
+                            env=build_env,
+                        )
+                    case _:
+                        assert_never(build_frontend)
 
                 built_wheel = next(built_wheel_dir.glob("*.whl"))
 
diff --git a/cibuildwheel/projectfiles.py b/cibuildwheel/projectfiles.py
index 3fcebe535..f9e2ef3b9 100644
--- a/cibuildwheel/projectfiles.py
+++ b/cibuildwheel/projectfiles.py
@@ -14,32 +14,16 @@ def get_parent(node: ast.AST | None, depth: int = 1) -> ast.AST | None:
 
 
 def is_main(parent: ast.AST | None) -> bool:
-    if parent is None:
-        return False
-
-    # This would be much nicer with 3.10's pattern matching!
-    if not isinstance(parent, ast.If):
-        return False
-    if not isinstance(parent.test, ast.Compare):
-        return False
-
-    try:
-        (op,) = parent.test.ops
-        (comp,) = parent.test.comparators
-    except ValueError:
-        return False
-
-    if not isinstance(op, ast.Eq):
-        return False
-
-    values = {comp, parent.test.left}
-
-    mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"}
-    if len(mains) != 1:
-        return False
-    consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"}
-
-    return len(consts) == 1
+    match parent:
+        case ast.If(test=ast.Compare(left=left, ops=[ast.Eq()], comparators=[comp])):
+            values = {left, comp}
+            mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"}
+            if len(mains) != 1:
+                return False
+            consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"}
+            return len(consts) == 1
+        case _:
+            return False
 
 
 class Analyzer(ast.NodeVisitor):
@@ -65,12 +49,10 @@ def visit_keyword(self, node: ast.keyword) -> None:
             parent is not None and get_parent(parent) is None and is_main(get_parent(node, 3))
         )
 
-        if (
-            node.arg == "python_requires"
-            and isinstance(node.value, ast.Constant)
-            and (unnested or name_main_unnested)
-        ):
-            self.requires_python = node.value.value
+        match node:
+            case ast.keyword(arg="python_requires", value=ast.Constant(value=version)):
+                if unnested or name_main_unnested:
+                    self.requires_python = version
 
 
 def setup_py_python_requires(content: str) -> str | None:

From 807cd8169974bbc80c66c631e2698ace0aaffb6c Mon Sep 17 00:00:00 2001
From: MichaIng <micha@dietpi.com>
Date: Tue, 8 Jul 2025 14:59:53 +0200
Subject: [PATCH 151/193] fix: typo in riscv64 CPython 3.9 identifier (#2490)

Signed-off-by: MichaIng <micha@dietpi.com>
---
 cibuildwheel/resources/build-platforms.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 7a5a7628e..647ae3d75 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -132,7 +132,7 @@ python_configurations = [
   { identifier = "cp314-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314" },
   { identifier = "cp314t-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
   { identifier = "cp38-musllinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
-  { identifier = "cp39-musllinux_ricv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
+  { identifier = "cp39-musllinux_riscv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
   { identifier = "cp310-musllinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
   { identifier = "cp311-musllinux_riscv64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
   { identifier = "cp312-musllinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },

From 75b1860702799558b0c2c8dd91f1db1928f5bc26 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 8 Jul 2025 09:00:18 -0400
Subject: [PATCH 152/193] [pre-commit.ci] pre-commit autoupdate (#2491)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.1 → v0.12.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.2)
- [github.com/python-jsonschema/check-jsonschema: 0.33.1 → 0.33.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.1...0.33.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 554869b01..e0a9aed32 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.12.1
+  rev: v0.12.2
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
@@ -80,7 +80,7 @@ repos:
 
 
 - repo: https://github.com/python-jsonschema/check-jsonschema
-  rev: 0.33.1
+  rev: 0.33.2
   hooks:
     - id: check-dependabot
     - id: check-github-actions

From 157d81eddb28ae34959756c5a3170f002ec305c1 Mon Sep 17 00:00:00 2001
From: Hanlu <liangmulu@outlook.com>
Date: Wed, 9 Jul 2025 20:57:38 +0800
Subject: [PATCH 153/193] chore: remove redundant word in comment (#2492)

Signed-off-by: liangmulu <liangmulu@outlook.com>
---
 test/test_testing.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test_testing.py b/test/test_testing.py
index 92de0e87e..de750eaef 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -189,7 +189,7 @@ def test_bare_pytest_invocation(
     tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str
 ) -> None:
     """
-    Check that if a user runs pytest in the the test cwd without setting
+    Check that if a user runs pytest in the test cwd without setting
     test-sources, it raises a helpful error
     """
     project_dir = tmp_path / "project"

From 5e245905b3eb0781684e6b9845c527e87bf552ae Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Wed, 9 Jul 2025 22:14:56 -0700
Subject: [PATCH 154/193] [Bot] Update dependencies (#2488)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   |  32 +-
 .../resources/constraints-pyodide312.txt      |   4 +-
 .../resources/constraints-pyodide313.txt      |   4 +-
 .../resources/constraints-python310.txt       |   2 +-
 .../resources/constraints-python311.txt       |   2 +-
 .../resources/constraints-python312.txt       |   2 +-
 .../resources/constraints-python313.txt       |   2 +-
 .../resources/constraints-python314.txt       |   2 +-
 .../resources/constraints-python39.txt        |   2 +-
 cibuildwheel/resources/constraints.txt        |   2 +-
 .../resources/pinned_docker_images.cfg        |  58 +--
 .../python-build-standalone-releases.json     | 450 +++++++++---------
 docs/working-examples.md                      |   4 +-
 13 files changed, 291 insertions(+), 275 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 647ae3d75..cbef21ca1 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -165,20 +165,20 @@ python_configurations = [
   { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
   { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
   { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
-  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
-  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
-  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
-  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
-  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
-  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b3-macos11.pkg" },
+  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
+  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
+  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
+  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
+  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
+  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
   { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
   { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
   { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
   { identifier = "pp39-macosx_arm64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2" },
   { identifier = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_x86_64.tar.bz2" },
   { identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
-  { identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_x86_64.tar.bz2" },
-  { identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_arm64.tar.bz2" },
+  { identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_x86_64.tar.bz2" },
+  { identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_arm64.tar.bz2" },
   { identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-amd64.tar.gz" },
   { identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-aarch64.tar.gz" },
 ]
@@ -199,29 +199,29 @@ python_configurations = [
   { identifier = "cp313t-win32", version = "3.13.5", arch = "32" },
   { identifier = "cp313-win_amd64", version = "3.13.5", arch = "64" },
   { identifier = "cp313t-win_amd64", version = "3.13.5", arch = "64" },
-  { identifier = "cp314-win32", version = "3.14.0-b3", arch = "32" },
-  { identifier = "cp314t-win32", version = "3.14.0-b3", arch = "32" },
-  { identifier = "cp314-win_amd64", version = "3.14.0-b3", arch = "64" },
-  { identifier = "cp314t-win_amd64", version = "3.14.0-b3", arch = "64" },
+  { identifier = "cp314-win32", version = "3.14.0-b4", arch = "32" },
+  { identifier = "cp314t-win32", version = "3.14.0-b4", arch = "32" },
+  { identifier = "cp314-win_amd64", version = "3.14.0-b4", arch = "64" },
+  { identifier = "cp314t-win_amd64", version = "3.14.0-b4", arch = "64" },
   { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
   { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
   { identifier = "cp313-win_arm64", version = "3.13.5", arch = "ARM64" },
   { identifier = "cp313t-win_arm64", version = "3.13.5", arch = "ARM64" },
-  { identifier = "cp314-win_arm64", version = "3.14.0-b3", arch = "ARM64" },
-  { identifier = "cp314t-win_arm64", version = "3.14.0-b3", arch = "ARM64" },
+  { identifier = "cp314-win_arm64", version = "3.14.0-b4", arch = "ARM64" },
+  { identifier = "cp314t-win_arm64", version = "3.14.0-b4", arch = "ARM64" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
-  { identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-win64.zip" },
+  { identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-win64.zip" },
   { identifier = "gp311_242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-windows-amd64.zip" },
 ]
 
 [pyodide]
 python_configurations = [
   { identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.7", node_version = "v22" },
-  { identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.28.0a3", node_version = "v22" },
+  { identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.28.0", node_version = "v22" },
 ]
 
 [ios]
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 990988404..f0dfa23f6 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -10,7 +10,7 @@ build==1.2.2.post1
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   pyodide-build
-certifi==2025.6.15
+certifi==2025.7.9
     # via
     #   httpcore
     #   httpx
@@ -94,7 +94,7 @@ typer==0.16.0
     #   auditwheel-emscripten
     #   pyodide-build
     #   pyodide-cli
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via
     #   anyio
     #   pydantic
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index 34832e66f..a2319f04e 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -10,7 +10,7 @@ build==1.2.2.post1
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   pyodide-build
-certifi==2025.6.15
+certifi==2025.7.9
     # via
     #   httpcore
     #   httpx
@@ -94,7 +94,7 @@ typer==0.16.0
     #   auditwheel-emscripten
     #   pyodide-build
     #   pyodide-cli
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via
     #   pydantic
     #   pydantic-core
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index ee7e66578..c02574796 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 4f0b01819..f13abf533 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index 4f0b01819..f13abf533 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index 4f0b01819..f13abf533 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python314.txt b/cibuildwheel/resources/constraints-python314.txt
index 4f0b01819..f13abf533 100644
--- a/cibuildwheel/resources/constraints-python314.txt
+++ b/cibuildwheel/resources/constraints-python314.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index ee7e66578..c02574796 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -26,7 +26,7 @@ pyproject-hooks==1.2.0
     # via build
 tomli==2.2.1
     # via build
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 4f0b01819..f13abf533 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -22,7 +22,7 @@ platformdirs==4.3.8
     # via virtualenv
 pyproject-hooks==1.2.0
     # via build
-typing-extensions==4.14.0
+typing-extensions==4.14.1
     # via delocate
 virtualenv==20.31.2
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index a6b550730..21c092349 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,49 +1,49 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.07-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.09-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.06.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.09-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.07-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.09-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.06.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.06.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.07-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.09-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.06.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.06.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.07-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.09-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.06.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.07-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.09-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.06.28-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.06.28-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.06.28-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.09-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.09-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.07-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.06.28-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.06.28-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.09-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.09-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 61a4bdc88..85439c260 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,439 +1,455 @@
 {
   "releases": [
     {
-      "tag": "20250626",
+      "tag": "20250708",
       "assets": [
         {
-          "name": "cpython-3.10.18+20250626-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.10.18%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.11.13%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.12.11%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.13.5%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b3+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.14.0b3%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250626/cpython-3.9.23%2B20250626-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.23+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.23+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.23+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+        },
+        {
+          "name": "cpython-3.9.23+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 3207a54bd..c85689170 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -31,8 +31,8 @@ title: Working examples
 | [pyinstrument][]                  | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python profiler with a C extension. No external dependencies. |
 | [scikit-image][]                  | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Image processing library. Uses cibuildwheel to build and test a project that uses Cython with platform-native code.  |
 | [PyOxidizer][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool |
-| [twisted-iocpsupport][]           | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
 | [cvxpy][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. |
+| [twisted-iocpsupport][]           | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
 | [pedalboard][]                    | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | A Python library for working with audio data and audio plugins by wrapping the [JUCE](https://github.com/juce-framework/JUCE/) C++ framework. Uses cibuildwheel to deploy on as many operating systems and Python versions as possible with only one dependency (any NumPy).  |
 | [websockets][]                    | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module.  |
 | [River][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python |
@@ -141,8 +141,8 @@ title: Working examples
 [pyinstrument]: https://github.com/joerick/pyinstrument
 [scikit-image]: https://github.com/scikit-image/scikit-image
 [PyOxidizer]: https://github.com/indygreg/PyOxidizer
-[twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
 [cvxpy]: https://github.com/cvxpy/cvxpy
+[twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
 [pedalboard]: https://github.com/spotify/pedalboard
 [websockets]: https://github.com/python-websockets/websockets
 [River]: https://github.com/online-ml/river

From cd9b48cb74a7f215d10ab74d9cf6a139f0279eec Mon Sep 17 00:00:00 2001
From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com>
Date: Sun, 13 Jul 2025 07:08:50 +0530
Subject: [PATCH 155/193] feat: Pyodide 0.28 release (#2487)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Bump testing version to 0.28.0

* Bump Pyodide constraints

* Add Pyodide 0.28.0 identifier

* Override to a different, alpha version for testing

* Update docs for Pyodide versions everywhere

* Add comment on using older Pyodide alpha version

* Bump to `"cp313*"` for compatibility

* Drop identifier for Pyodide 0.28.0a3

* Add back Pyodide 0.28.0a3 identifier

* Add helpful comment about pyodide-prerelease option

* Fix test and add a comment about it

* Keep one Pyodide version per Python version

* Fix docs for `pyodide-prerelease`; Python ➡️ Pyodide

* Update utils.py

* Drop CI test step for overridden Pyodide version

Suggested-by: Joe Rickerby <joerick@mac.com>

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
---
 .github/workflows/test.yml       | 13 -------------
 cibuildwheel/selector.py         | 15 +++++++++++----
 docs/options.md                  | 16 ++++++++--------
 docs/platforms.md                |  2 +-
 test/utils.py                    |  4 +---
 unit_test/build_selector_test.py |  2 +-
 unit_test/options_test.py        |  4 ++--
 7 files changed, 24 insertions(+), 32 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b99ee74d1..229bce922 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -233,19 +233,6 @@ jobs:
       env:
         CIBW_PLATFORM: pyodide
 
-    - name: Run a sample build (GitHub Action) for an overridden Pyodide version
-      uses: ./
-      with:
-        package-dir: sample_proj
-        output-dir: wheelhouse
-        # In case this breaks at any point in time, switch to using the latest version
-        # available or any other version that is not the same as the default one set
-        # in cibuildwheel/resources/build-platforms.toml.
-      env:
-        CIBW_PLATFORM: pyodide
-        CIBW_BUILD: "cp312*"
-        CIBW_PYODIDE_VERSION: "0.27.6"
-
     - name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
       run: |
         uv run --no-sync ./bin/run_tests.py
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 256e4fb04..f0bce928a 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -99,10 +99,17 @@ def __call__(self, build_id: str) -> bool:
             return False
         if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"):
             return False
-        if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
-            build_id, "cp313-pyodide_*"
-        ):
-            return False
+        # TODO: Re-enable this when we have Pyodide prereleases again (e.g., 0.29.0a1+)
+        # Python 3.13 support became stable in Pyodide 0.28.0, so it no longer needs a prerelease
+        # flag.
+        # Also update Pyodide tests in unit_test/build_selector_test.py accordingly.
+        # When re-enabling, update the pattern to match the experimental Python version in case
+        # it is bumped to Python 3.14 (likely cp314-pyodide_* but could remain as 3.13 as well).
+        # This depends on the CPython version being used in the Pyodide runtime at the time.
+        # if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
+        #     build_id, "cp313-pyodide_*"
+        # ):
+        #     return False
 
         should_build = selector_matches(self.build_config, build_id)
         should_skip = selector_matches(self.skip_config, build_id)
diff --git a/docs/options.md b/docs/options.md
index ed2fadcba..d545adc19 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1288,22 +1288,22 @@ The available Pyodide versions are determined by the version of `pyodide-build`
 
     ```toml
     [tool.cibuildwheel.pyodide]
-    # Build Pyodide wheels using Pyodide version 0.27.6
-    pyodide-version = "0.27.6"
+    # Build Pyodide wheels using Pyodide version 0.28.0
+    pyodide-version = "0.28.0"
 
     [tool.cibuildwheel.pyodide]
-    # Build Pyodide wheels using a specific alpha release
-    pyodide-version = "0.28.0a2"
+    # Build Pyodide wheels using a specific, previously released alpha release of 0.28.0
+    pyodide-version = "0.28.0a3"
     ```
 
 !!! tab examples "Environment variables"
 
     ```yaml
-    # Build Pyodide wheels using Pyodide version 0.27.6
-    CIBW_PYODIDE_VERSION: 0.27.6
+    # Build Pyodide wheels using Pyodide version 0.28.0
+    CIBW_PYODIDE_VERSION: 0.28.0
 
-    # Build Pyodide wheels using a specific alpha release
-    CIBW_PYODIDE_VERSION: 0.28.0a2
+    # Build Pyodide wheels using a specific, previously released alpha release of 0.28.0
+    CIBW_PYODIDE_VERSION: 0.28.0a3
     ```
 
 
diff --git a/docs/platforms.md b/docs/platforms.md
index 84a8cb502..17cc40a88 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -172,7 +172,7 @@ You must target pyodide with `--platform pyodide` (or use `--only` on the identi
 
 It is also possible to target a specific Pyodide version by setting the [`pyodide-version`](options.md#pyodide-version) option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
 
-If there are pre-releases available for a newer Python version, the `pyodide-prerelease` [`enable`](options.md#enable) can be used to include pre-release versions.
+If there are pre-releases available for a newer Pyodide version, the `pyodide-prerelease` [`enable`](options.md#enable) can be used to include pre-release versions.
 
 ### Running tests
 
diff --git a/test/utils.py b/test/utils.py
index 749e54dc1..ccd467aa9 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -261,9 +261,7 @@ def _expected_wheels(
         musllinux_versions = ["musllinux_1_2"]
 
     if platform == "pyodide" and python_abi_tags is None:
-        python_abi_tags = ["cp312-cp312"]
-        if EnableGroup.PyodidePrerelease in enable_groups:
-            python_abi_tags.append("cp313-cp313")
+        python_abi_tags = ["cp312-cp312", "cp313-cp313"]
     elif platform == "ios" and python_abi_tags is None:
         python_abi_tags = ["cp313-cp313"]
     elif python_abi_tags is None:
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index 601fe25f5..4c723244d 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -107,7 +107,7 @@ def test_build_filter_pyodide():
         enable=frozenset(),
     )
     assert build_selector("cp312-pyodide_wasm32")
-    assert not build_selector("cp313-pyodide_wasm32")
+    assert build_selector("cp313-pyodide_wasm32")
 
 
 def test_skip():
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index 8bf7e5691..d1207aa15 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -35,7 +35,7 @@
 
 environment-pass = ["EXAMPLE_ENV"]
 
-pyodide-version = "0.27.6"
+pyodide-version = "0.28.0"
 
 [tool.cibuildwheel.macos]
 test-requires = "else"
@@ -92,7 +92,7 @@ def test_options_1(tmp_path, monkeypatch):
     assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux_2_34"]
 
     local = options.build_options("cp312-pyodide_wasm32")
-    assert local.pyodide_version == "0.27.6"
+    assert local.pyodide_version == "0.28.0"
 
 
 def test_passthrough(tmp_path, monkeypatch):

From 576ebccc7830d5e496c04222518255636778619d Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 14 Jul 2025 01:31:16 -0700
Subject: [PATCH 156/193] [Bot] Update dependencies (#2496)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |   2 +-
 .../resources/constraints-pyodide313.txt      |   2 +-
 .../resources/pinned_docker_images.cfg        |  58 +--
 .../python-build-standalone-releases.json     | 450 +++++++++---------
 docs/working-examples.md                      |  12 +-
 5 files changed, 262 insertions(+), 262 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index f0dfa23f6..46e762e70 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -10,7 +10,7 @@ build==1.2.2.post1
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   pyodide-build
-certifi==2025.7.9
+certifi==2025.7.14
     # via
     #   httpcore
     #   httpx
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index a2319f04e..5d2649499 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -10,7 +10,7 @@ build==1.2.2.post1
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   pyodide-build
-certifi==2025.7.9
+certifi==2025.7.14
     # via
     #   httpcore
     #   httpx
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 21c092349..d977c90b7 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,49 +1,49 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.07-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.12-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.09-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.12-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.07-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.12-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.07-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.12-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.07-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.12-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.07-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.12-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.09-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.12-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.09-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.09-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.07-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.12-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.12-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.12-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.09-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.09-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.12-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.12-1
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index 85439c260..fa09609fa 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,455 +1,455 @@
 {
   "releases": [
     {
-      "tag": "20250708",
+      "tag": "20250712",
       "assets": [
         {
-          "name": "cpython-3.10.18+20250708-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.10.18%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.11.13%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.12.11%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.13.5%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.14.0b4%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0b4+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250708/cpython-3.9.23%2B20250708-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index c85689170..4f3caaa17 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -81,12 +81,12 @@ title: Working examples
 | [sourmash][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
 | [abess][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
 | [python-snappy][]                 | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library |
-| [cyvcf2][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
 | [jq.py][]                         | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
+| [cyvcf2][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
 | [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [Tokenizer][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
-| [iminuit][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
 | [Confluent client for Kafka][]    | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
+| [iminuit][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
 | [pillow-heif][]                   | ![github icon][] ![cirrusci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Bindings to libheif library with third party dependencies. Fully automated CI for tests and publishing including Apple Silicon builds. |
 | [keyvi][]                         | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime |
 | [PyGLM][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python |
@@ -111,8 +111,8 @@ title: Working examples
 | [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
 | [ril][]                           | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. |
 | [SiPM][]                          | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
-| [aalink][]                        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
 | [GSD][]                           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
+| [aalink][]                        | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Async Python interface for Ableton Link. |
 | [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
 | [xmlstarlet][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. |
 | [werpy][]                         | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | An ultra-fast python package using optimized dynamic programming to compute the Word Error Rate (WER). |
@@ -191,12 +191,12 @@ title: Working examples
 [sourmash]: https://github.com/sourmash-bio/sourmash
 [abess]: https://github.com/abess-team/abess
 [python-snappy]: https://github.com/intake/python-snappy
-[cyvcf2]: https://github.com/brentp/cyvcf2
 [jq.py]: https://github.com/mwilliamson/jq.py
+[cyvcf2]: https://github.com/brentp/cyvcf2
 [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [Tokenizer]: https://github.com/OpenNMT/Tokenizer
-[iminuit]: https://github.com/scikit-hep/iminuit
 [Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
+[iminuit]: https://github.com/scikit-hep/iminuit
 [pillow-heif]: https://github.com/bigcat88/pillow_heif
 [keyvi]: https://github.com/KeyviDev/keyvi
 [PyGLM]: https://github.com/Zuzu-Typ/PyGLM
@@ -221,8 +221,8 @@ title: Working examples
 [pyjet]: https://github.com/scikit-hep/pyjet
 [ril]: https://github.com/Cryptex-github/ril-py
 [SiPM]: https://github.com/EdoPro98/SimSiPM
-[aalink]: https://github.com/artfwo/aalink
 [GSD]: https://github.com/glotzerlab/gsd
+[aalink]: https://github.com/artfwo/aalink
 [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
 [xmlstarlet]: https://github.com/dimitern/xmlstarlet
 [werpy]: https://github.com/analyticsinmotion/werpy

From a2c263d4ab31ffee42325392fe537522259c60f2 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 14 Jul 2025 21:34:17 -0700
Subject: [PATCH 157/193] [pre-commit.ci] pre-commit autoupdate (#2497)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.2 → v0.12.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.2...v0.12.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e0a9aed32..3268898d4 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.12.2
+  rev: v0.12.3
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From d0e4aded3ea5a3995feb1a18ec515837f474197c Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Tue, 15 Jul 2025 20:45:53 -0700
Subject: [PATCH 158/193] feat: add summary to Action (#2469)

* chore: add summary to Action

* refactor: new summary table

* fix: fixup tests and formatting

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: pyodide missing some logging

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: nicer printout, nicer in-place summary

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: use summary for everything

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: support only one output wheel from repair

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Add new Github summary format

* Remove a couple of humanize uses

* fix: filter ANSI codes in summary

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: add sha256

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: nicer wheel/wheels depending on how many are present

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
---
 .pre-commit-config.yaml           |   1 +
 cibuildwheel/__main__.py          |  41 +------
 cibuildwheel/ci.py                |  14 ++-
 cibuildwheel/logger.py            | 174 ++++++++++++++++++++++++++----
 cibuildwheel/options.py           |  25 ++++-
 cibuildwheel/platforms/ios.py     |   3 +-
 cibuildwheel/platforms/linux.py   |   6 +-
 cibuildwheel/platforms/macos.py   |   3 +-
 cibuildwheel/platforms/pyodide.py |   3 +-
 cibuildwheel/platforms/windows.py |   3 +-
 pyproject.toml                    |   1 +
 unit_test/main_tests/conftest.py  |   5 +-
 unit_test/option_prepare_test.py  |   2 +-
 unit_test/wheel_print_test.py     |  45 ++++----
 14 files changed, 231 insertions(+), 95 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3268898d4..0bdea9190 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -29,6 +29,7 @@ repos:
     additional_dependencies: &mypy-dependencies
       - bracex
       - dependency-groups>=1.2
+      - humanize
       - nox>=2025.2.9
       - orjson
       - packaging
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index 934d80c4e..7585ca830 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -7,10 +7,9 @@
 import sys
 import tarfile
 import textwrap
-import time
 import traceback
 import typing
-from collections.abc import Generator, Iterable, Sequence
+from collections.abc import Iterable, Sequence
 from pathlib import Path
 from tempfile import mkdtemp
 from typing import Any, Literal, TextIO
@@ -286,42 +285,6 @@ def _compute_platform(args: CommandLineArguments) -> PlatformName:
     return _compute_platform_auto()
 
 
-@contextlib.contextmanager
-def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
-    """
-    Prints the new items in a directory upon exiting. The message to display
-    can include {n} for number of wheels, {s} for total number of seconds,
-    and/or {m} for total number of minutes. Does not print anything if this
-    exits via exception.
-    """
-
-    start_time = time.time()
-    existing_contents = set(output_dir.iterdir())
-    yield
-    final_contents = set(output_dir.iterdir())
-
-    new_contents = [
-        FileReport(wheel.name, f"{(wheel.stat().st_size + 1023) // 1024:,d}")
-        for wheel in final_contents - existing_contents
-    ]
-
-    if not new_contents:
-        return
-
-    max_name_len = max(len(f.name) for f in new_contents)
-    max_size_len = max(len(f.size) for f in new_contents)
-    n = len(new_contents)
-    s = time.time() - start_time
-    m = s / 60
-    print(
-        msg.format(n=n, s=s, m=m),
-        *sorted(
-            f"  {f.name:<{max_name_len}s}   {f.size:>{max_size_len}s} kB" for f in new_contents
-        ),
-        sep="\n",
-    )
-
-
 def build_in_directory(args: CommandLineArguments) -> None:
     platform: PlatformName = _compute_platform(args)
     if platform == "pyodide" and sys.platform == "win32":
@@ -382,7 +345,7 @@ def build_in_directory(args: CommandLineArguments) -> None:
 
     tmp_path = Path(mkdtemp(prefix="cibw-run-")).resolve(strict=True)
     try:
-        with print_new_wheels("\n{n} wheels produced in {m:.0f} minutes:", output_dir):
+        with log.print_summary(options=options):
             platform_module.build(options, tmp_path)
     finally:
         # avoid https://github.com/python/cpython/issues/86962 by performing
diff --git a/cibuildwheel/ci.py b/cibuildwheel/ci.py
index 03205b7a4..500d435eb 100644
--- a/cibuildwheel/ci.py
+++ b/cibuildwheel/ci.py
@@ -4,6 +4,8 @@
 
 from .util.helpers import strtobool
 
+ANSI_CODE_REGEX = re.compile(r"(\033\[[0-9;]*m)")
+
 
 class CIProvider(Enum):
     # official support
@@ -46,7 +48,7 @@ def fix_ansi_codes_for_github_actions(text: str) -> str:
     Github Actions forgets the current ANSI style on every new line. This
     function repeats the current ANSI style on every new line.
     """
-    ansi_code_regex = re.compile(r"(\033\[[0-9;]*m)")
+
     ansi_codes: list[str] = []
     output = ""
 
@@ -55,7 +57,7 @@ def fix_ansi_codes_for_github_actions(text: str) -> str:
         output += "".join(ansi_codes) + line
 
         # split the line at each ANSI code
-        parts = ansi_code_regex.split(line)
+        parts = ANSI_CODE_REGEX.split(line)
         # if there are any ANSI codes, save them
         if len(parts) > 1:
             # iterate over the ANSI codes in this line
@@ -67,3 +69,11 @@ def fix_ansi_codes_for_github_actions(text: str) -> str:
                     ansi_codes.append(code)
 
     return output
+
+
+def filter_ansi_codes(text: str, /) -> str:
+    """
+    Remove ANSI codes from text.
+    """
+
+    return ANSI_CODE_REGEX.sub("", text)
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index b2a9a3115..6ed3b761c 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -1,11 +1,24 @@
 import codecs
+import contextlib
+import dataclasses
+import functools
+import hashlib
+import io
 import os
 import re
 import sys
+import textwrap
 import time
-from typing import IO, AnyStr, Final, Literal
+from collections.abc import Generator
+from pathlib import Path
+from typing import IO, TYPE_CHECKING, AnyStr, Final, Literal
 
-from .ci import CIProvider, detect_ci_provider
+import humanize
+
+from .ci import CIProvider, detect_ci_provider, filter_ansi_codes
+
+if TYPE_CHECKING:
+    from .options import Options
 
 FoldPattern = tuple[str, str]
 DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "")
@@ -69,6 +82,33 @@ def __init__(self, *, unicode: bool) -> None:
         self.error = "✕" if unicode else "failed"
 
 
+@dataclasses.dataclass(kw_only=True, frozen=True)
+class BuildInfo:
+    identifier: str
+    filename: Path | None
+    duration: float
+
+    @functools.cached_property
+    def size(self) -> str | None:
+        if self.filename is None:
+            return None
+        return humanize.naturalsize(self.filename.stat().st_size)
+
+    @functools.cached_property
+    def sha256(self) -> str | None:
+        if self.filename is None:
+            return None
+        with self.filename.open("rb") as f:
+            digest = hashlib.file_digest(f, "sha256")
+        return digest.hexdigest()
+
+    def __str__(self) -> str:
+        duration = humanize.naturaldelta(self.duration)
+        if self.filename:
+            return f"{self.identifier}: {self.filename.name} {self.size} in {duration}, SHA256={self.sha256}"
+        return f"{self.identifier}: {duration} (test only)"
+
+
 class Logger:
     fold_mode: Literal["azure", "github", "travis", "disabled"]
     colors_enabled: bool
@@ -77,6 +117,7 @@ class Logger:
     build_start_time: float | None = None
     step_start_time: float | None = None
     active_fold_group_name: str | None = None
+    summary: list[BuildInfo]
 
     def __init__(self) -> None:
         if sys.platform == "win32" and hasattr(sys.stdout, "reconfigure"):
@@ -88,25 +129,28 @@ def __init__(self) -> None:
 
         ci_provider = detect_ci_provider()
 
-        if ci_provider == CIProvider.azure_pipelines:
-            self.fold_mode = "azure"
-            self.colors_enabled = True
+        match ci_provider:
+            case CIProvider.azure_pipelines:
+                self.fold_mode = "azure"
+                self.colors_enabled = True
 
-        elif ci_provider == CIProvider.github_actions:
-            self.fold_mode = "github"
-            self.colors_enabled = True
+            case CIProvider.github_actions:
+                self.fold_mode = "github"
+                self.colors_enabled = True
 
-        elif ci_provider == CIProvider.travis_ci:
-            self.fold_mode = "travis"
-            self.colors_enabled = True
+            case CIProvider.travis_ci:
+                self.fold_mode = "travis"
+                self.colors_enabled = True
 
-        elif ci_provider == CIProvider.appveyor:
-            self.fold_mode = "disabled"
-            self.colors_enabled = True
+            case CIProvider.appveyor:
+                self.fold_mode = "disabled"
+                self.colors_enabled = True
 
-        else:
-            self.fold_mode = "disabled"
-            self.colors_enabled = file_supports_color(sys.stdout)
+            case _:
+                self.fold_mode = "disabled"
+                self.colors_enabled = file_supports_color(sys.stdout)
+
+        self.summary = []
 
     def build_start(self, identifier: str) -> None:
         self.step_end()
@@ -120,7 +164,7 @@ def build_start(self, identifier: str) -> None:
         self.build_start_time = time.time()
         self.active_build_identifier = identifier
 
-    def build_end(self) -> None:
+    def build_end(self, filename: Path | None) -> None:
         assert self.build_start_time is not None
         assert self.active_build_identifier is not None
         self.step_end()
@@ -128,11 +172,14 @@ def build_end(self) -> None:
         c = self.colors
         s = self.symbols
         duration = time.time() - self.build_start_time
+        duration_str = humanize.naturaldelta(duration, minimum_unit="milliseconds")
 
         print()
-        print(
-            f"{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s"
+        print(f"{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration_str}")
+        self.summary.append(
+            BuildInfo(identifier=self.active_build_identifier, filename=filename, duration=duration)
         )
+
         self.build_start_time = None
         self.active_build_identifier = None
 
@@ -147,6 +194,7 @@ def step_end(self, success: bool = True) -> None:
             c = self.colors
             s = self.symbols
             duration = time.time() - self.step_start_time
+
             if success:
                 print(f"{c.green}{s.done} {c.end}{duration:.2f}s".rjust(78))
             else:
@@ -183,6 +231,26 @@ def error(self, error: BaseException | str) -> None:
             c = self.colors
             print(f"cibuildwheel: {c.bright_red}error{c.end}: {error}\n", file=sys.stderr)
 
+    @contextlib.contextmanager
+    def print_summary(self, *, options: "Options") -> Generator[None, None, None]:
+        start = time.time()
+        yield
+        duration = time.time() - start
+        if summary_path := os.environ.get("GITHUB_STEP_SUMMARY"):
+            github_summary = self._github_step_summary(duration=duration, options=options)
+            Path(summary_path).write_text(filter_ansi_codes(github_summary), encoding="utf-8")
+
+        n = len(self.summary)
+        s = "s" if n > 1 else ""
+        duration_str = humanize.naturaldelta(duration)
+        print()
+        self._start_fold_group(f"{n} wheel{s} produced in {duration_str}")
+        for build_info in self.summary:
+            print(" ", build_info)
+        self._end_fold_group()
+
+        self.summary = []
+
     @property
     def step_active(self) -> bool:
         return self.step_start_time is not None
@@ -222,6 +290,72 @@ def _fold_group_identifier(name: str) -> str:
         # lowercase, shorten
         return identifier.lower()[:20]
 
+    def _github_step_summary(self, duration: float, options: "Options") -> str:
+        """
+        Returns the GitHub step summary, in markdown format.
+        """
+        out = io.StringIO()
+        options_summary = options.summary(
+            identifiers=[bi.identifier for bi in self.summary], skip_unset=True
+        )
+        out.write(
+            textwrap.dedent("""\
+                ### 🎡 cibuildwheel
+
+                <details>
+                <summary>
+                Build options
+                </summary>
+
+                ```yaml
+                {options_summary}
+                ```
+
+                </details>
+
+            """).format(options_summary=options_summary)
+        )
+        n_wheels = len([b for b in self.summary if b.filename])
+        wheel_rows = "\n".join(
+            "<tr>"
+            f"<td nowrap>{'<samp>' + b.filename.name + '</samp>' if b.filename else '*Build only*'}</td>"
+            f"<td nowrap>{b.size or 'N/A'}</td>"
+            f"<td nowrap><samp>{b.identifier}</samp></td>"
+            f"<td nowrap>{humanize.naturaldelta(b.duration)}</td>"
+            f"<td nowrap><samp>{b.sha256 or 'N/A'}</samp></td>"
+            "</tr>"
+            for b in self.summary
+        )
+        out.write(
+            textwrap.dedent("""\
+                <table>
+                <thead>
+                <tr>
+                <th align="left">Wheel</th>
+                <th align="left">Size</th>
+                <th align="left">Build identifier</th>
+                <th align="left">Time</th>
+                <th align="left">SHA256</th>
+                </tr>
+                </thead>
+                <tbody>
+                {wheel_rows}
+                </tbody>
+                </table>
+                <div align="right"><sup>{n} wheel{s} created in {duration_str}</sup></div>
+            """).format(
+                wheel_rows=wheel_rows,
+                n=n_wheels,
+                duration_str=humanize.naturaldelta(duration),
+                s="s" if n_wheels > 1 else "",
+            )
+        )
+
+        out.write("\n")
+        out.write("---")
+        out.write("\n")
+        return out.getvalue()
+
     @property
     def colors(self) -> Colors:
         return Colors(enabled=self.colors_enabled)
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index e88998b29..6656df008 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -907,14 +907,18 @@ def defaults(self) -> Self:
             defaults=True,
         )
 
-    def summary(self, identifiers: Iterable[str]) -> str:
+    def summary(self, identifiers: Iterable[str], skip_unset: bool = False) -> str:
         lines = []
         global_option_names = sorted(f.name for f in dataclasses.fields(self.globals))
 
         for option_name in global_option_names:
             option_value = getattr(self.globals, option_name)
             default_value = getattr(self.defaults.globals, option_name)
-            lines.append(self.option_summary(option_name, option_value, default_value))
+            line = self.option_summary(
+                option_name, option_value, default_value, skip_unset=skip_unset
+            )
+            if line is not None:
+                lines.append(line)
 
         build_options = self.build_options(identifier=None)
         build_options_defaults = self.defaults.build_options(identifier=None)
@@ -934,9 +938,15 @@ def summary(self, identifiers: Iterable[str]) -> str:
                 i: getattr(build_options_for_identifier[i], option_name) for i in identifiers
             }
 
-            lines.append(
-                self.option_summary(option_name, option_value, default_value, overrides=overrides)
+            line = self.option_summary(
+                option_name,
+                option_value,
+                default_value,
+                overrides=overrides,
+                skip_unset=skip_unset,
             )
+            if line is not None:
+                lines.append(line)
 
         return "\n".join(lines)
 
@@ -946,7 +956,8 @@ def option_summary(
         option_value: Any,
         default_value: Any,
         overrides: Mapping[str, Any] | None = None,
-    ) -> str:
+        skip_unset: bool = False,
+    ) -> str | None:
         """
         Return a summary of the option value, including any overrides, with
         ANSI 'dim' color if it's the default.
@@ -960,6 +971,10 @@ def option_summary(
         overrides_value_strs = {k: v for k, v in overrides_value_strs.items() if v != value_str}
 
         has_been_set = (value_str != default_value_str) or overrides_value_strs
+
+        if skip_unset and not has_been_set:
+            return None
+
         c = log.colors
 
         result = c.gray if not has_been_set else ""
diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index b0511a2a4..f4cf6b60d 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -673,6 +673,7 @@ def build(options: Options, tmp_path: Path) -> None:
                     log.step_end()
 
             # We're all done here; move it to output (overwrite existing)
+            output_wheel: Path | None = None
             if compatible_wheel is None:
                 output_wheel = build_options.output_dir.joinpath(built_wheel.name)
                 moved_wheel = move_file(built_wheel, output_wheel)
@@ -685,7 +686,7 @@ def build(options: Options, tmp_path: Path) -> None:
             # Clean up
             shutil.rmtree(identifier_tmp_dir)
 
-            log.build_end()
+            log.build_end(output_wheel)
     except subprocess.CalledProcessError as error:
         msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
         raise errors.FatalError(msg) from error
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 1a388d95f..8d25d73d0 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -414,13 +414,15 @@ def build_in_container(
             # clean up test environment
             container.call(["rm", "-rf", testing_temp_dir])
 
-        # move repaired wheels to output
+        # move repaired wheel to output
+        output_wheel: Path | None = None
         if compatible_wheel is None:
             container.call(["mkdir", "-p", container_output_dir])
             container.call(["mv", repaired_wheel, container_output_dir])
             built_wheels.append(container_output_dir / repaired_wheel.name)
+            output_wheel = options.globals.output_dir / repaired_wheel.name
 
-        log.build_end()
+        log.build_end(output_wheel)
 
     log.step("Copying wheels back to host...")
     # copy the output back into the host
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index cc0a5a8ef..a273f062c 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -732,6 +732,7 @@ def build(options: Options, tmp_path: Path) -> None:
                     shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
 
             # we're all done here; move it to output (overwrite existing)
+            output_wheel = None
             if compatible_wheel is None:
                 output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
                 moved_wheel = move_file(repaired_wheel, output_wheel)
@@ -744,7 +745,7 @@ def build(options: Options, tmp_path: Path) -> None:
             # clean up
             shutil.rmtree(identifier_tmp_dir)
 
-            log.build_end()
+            log.build_end(output_wheel)
     except subprocess.CalledProcessError as error:
         msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
         raise errors.FatalError(msg) from error
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index 1ae5146b1..c46248ee7 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -540,6 +540,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
 
             # we're all done here; move it to output (overwrite existing)
+            output_wheel: Path | None = None
             if compatible_wheel is None:
                 output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
                 moved_wheel = move_file(repaired_wheel, output_wheel)
@@ -548,7 +549,7 @@ def build(options: Options, tmp_path: Path) -> None:
                         f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
                     )
                 built_wheels.append(output_wheel)
-            log.build_end()
+            log.build_end(output_wheel)
 
     except subprocess.CalledProcessError as error:
         msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 6913092cd..5a55b3a08 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -613,6 +613,7 @@ def build(options: Options, tmp_path: Path) -> None:
                 shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
 
             # we're all done here; move it to output (remove if already exists)
+            output_wheel = None
             if compatible_wheel is None:
                 output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
                 moved_wheel = move_file(repaired_wheel, output_wheel)
@@ -627,7 +628,7 @@ def build(options: Options, tmp_path: Path) -> None:
             # don't want to abort a build because of that)
             shutil.rmtree(identifier_tmp_dir, ignore_errors=True)
 
-            log.build_end()
+            log.build_end(output_wheel)
     except subprocess.CalledProcessError as error:
         msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
         raise errors.FatalError(msg) from error
diff --git a/pyproject.toml b/pyproject.toml
index b5665bc2e..55b254b0f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,6 +42,7 @@ dependencies = [
     "certifi",
     "dependency-groups>=1.2",
     "filelock",
+    "humanize",
     "packaging>=20.9",
     "platformdirs"
 ]
diff --git a/unit_test/main_tests/conftest.py b/unit_test/main_tests/conftest.py
index e01c8c027..ef5c08340 100644
--- a/unit_test/main_tests/conftest.py
+++ b/unit_test/main_tests/conftest.py
@@ -6,7 +6,8 @@
 
 import pytest
 
-from cibuildwheel import __main__, architecture
+from cibuildwheel import architecture
+from cibuildwheel.logger import Logger
 from cibuildwheel.platforms import linux, macos, pyodide, windows
 from cibuildwheel.util import file
 
@@ -58,7 +59,7 @@ def disable_print_wheels(monkeypatch):
     def empty_cm(*args, **kwargs):
         yield
 
-    monkeypatch.setattr(__main__, "print_new_wheels", empty_cm)
+    monkeypatch.setattr(Logger, "print_summary", empty_cm)
 
 
 @pytest.fixture
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 945cb3b0a..39d6058ab 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -45,7 +45,7 @@ def ignore_context_call(*args, **kwargs):
         "cibuildwheel.platforms.linux.build_in_container",
         mock.Mock(spec=platforms.linux.build_in_container),
     )
-    monkeypatch.setattr("cibuildwheel.__main__.print_new_wheels", ignore_context_call)
+    monkeypatch.setattr("cibuildwheel.logger.Logger.print_summary", ignore_context_call)
 
 
 @pytest.mark.usefixtures("mock_build_container", "fake_package_dir")
diff --git a/unit_test/wheel_print_test.py b/unit_test/wheel_print_test.py
index c3e4933af..9fd8dfcc5 100644
--- a/unit_test/wheel_print_test.py
+++ b/unit_test/wheel_print_test.py
@@ -1,34 +1,39 @@
+from pathlib import Path
+
 import pytest
 
-from cibuildwheel.__main__ import print_new_wheels
+from cibuildwheel.logger import BuildInfo, Logger
+from cibuildwheel.options import CommandLineArguments, Options
+
+OPTIONS_DEFAULTS = Options("linux", CommandLineArguments.defaults(), {}, defaults=True)
+FILE = Path(__file__)
 
 
-def test_printout_wheels(tmp_path, capsys):
-    tmp_path.joinpath("example.0").touch()
-    with print_new_wheels("TEST_MSG: {n}", tmp_path):
-        tmp_path.joinpath("example.1").write_bytes(b"0" * 1023)
-        tmp_path.joinpath("example.2").write_bytes(b"0" * 1025)
+def test_printout_wheels(capsys):
+    log = Logger()
+    log.fold_mode = "disabled"
+    log.colors_enabled = False
+
+    with log.print_summary(options=OPTIONS_DEFAULTS):
+        log.summary = [
+            BuildInfo(identifier="id1", filename=None, duration=3),
+            BuildInfo(identifier="id2", filename=FILE, duration=2),
+        ]
 
     captured = capsys.readouterr()
     assert captured.err == ""
 
-    assert "example.0" not in captured.out
-    assert "example.1   1 kB\n" in captured.out
-    assert "example.2   2 kB\n" in captured.out
-    assert "TEST_MSG:" in captured.out
-    assert "TEST_MSG: 2\n" in captured.out
+    assert "id1" in captured.out
+    assert "id2" in captured.out
+    assert "wheels produced in" in captured.out
+    assert "SHA256=" in captured.out
 
 
-def test_no_printout_on_error(tmp_path, capsys):
-    tmp_path.joinpath("example.0").touch()
-    with pytest.raises(RuntimeError), print_new_wheels("TEST_MSG: {n}", tmp_path):  # noqa: PT012
-        tmp_path.joinpath("example.1").touch()
+def test_no_printout_on_error(capsys):
+    log = Logger()
+    with pytest.raises(RuntimeError), log.print_summary(options=OPTIONS_DEFAULTS):
         raise RuntimeError()
 
     captured = capsys.readouterr()
     assert captured.err == ""
-
-    assert "example.0" not in captured.out
-    assert "example.1" not in captured.out
-    assert "example.2" not in captured.out
-    assert "TEST_MSG:" not in captured.out
+    assert captured.out == ""

From 7247e3887c10a6803d6e6bd300a967ceeb5be767 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Wed, 16 Jul 2025 15:00:49 -0700
Subject: [PATCH 159/193] fix: show options for --only (#2499)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/__main__.py       | 3 +++
 cibuildwheel/util/resources.py | 9 ++++++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index 7585ca830..60849857a 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -26,6 +26,7 @@
 from cibuildwheel.typing import PLATFORMS, PlatformName
 from cibuildwheel.util.file import CIBW_CACHE_PATH
 from cibuildwheel.util.helpers import strtobool
+from cibuildwheel.util.resources import read_all_configs
 
 
 @dataclasses.dataclass
@@ -132,6 +133,8 @@ def main_inner(global_options: GlobalOptions) -> None:
     parser.add_argument(
         "--only",
         default=None,
+        choices=[v["identifier"] for vv in read_all_configs().values() for v in vv],
+        metavar="IDENTIFIER",
         help="""
             Force a single wheel build when given an identifier. Overrides
             CIBW_BUILD/CIBW_SKIP. --platform and --arch cannot be specified
diff --git a/cibuildwheel/util/resources.py b/cibuildwheel/util/resources.py
index 0cb09a26d..4be747a96 100644
--- a/cibuildwheel/util/resources.py
+++ b/cibuildwheel/util/resources.py
@@ -22,8 +22,11 @@
 
 # this value is cached because it's used a lot in unit tests
 @functools.cache
-def read_python_configs(config: PlatformName) -> list[dict[str, str]]:
+def read_all_configs() -> dict[str, list[dict[str, str]]]:
     with BUILD_PLATFORMS.open("rb") as f:
         loaded_file = tomllib.load(f)
-    results: list[dict[str, str]] = list(loaded_file[config]["python_configurations"])
-    return results
+    return {k: list[dict[str, str]](v["python_configurations"]) for k, v in loaded_file.items()}
+
+
+def read_python_configs(config: PlatformName) -> list[dict[str, str]]:
+    return read_all_configs()[config]

From 9613645abc6d1b28103b7b93be4ab0148e52780a Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Fri, 18 Jul 2025 20:30:42 +0200
Subject: [PATCH 160/193] feat: add support for manylinux_2_34 i686 image
 (#2500)

---
 bin/update_docker.py                          | 12 +++-
 .../resources/pinned_docker_images.cfg        | 60 ++++++++++---------
 docs/options.md                               |  1 -
 test/test_manylinuxXXXX_only.py               |  9 ---
 4 files changed, 42 insertions(+), 40 deletions(-)

diff --git a/bin/update_docker.py b/bin/update_docker.py
index 20573a7fc..ad43307fa 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -59,7 +59,17 @@ def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None
     PyPAImage("manylinux_2_31", ["armv7l"]),
     # manylinux_2_34 images
     PyPAImage(
-        "manylinux_2_34", ["x86_64", "aarch64", "ppc64le", "s390x", "pypy_x86_64", "pypy_aarch64"]
+        "manylinux_2_34",
+        [
+            "x86_64",
+            "i686",
+            "aarch64",
+            "ppc64le",
+            "s390x",
+            "pypy_x86_64",
+            "pypy_i686",
+            "pypy_aarch64",
+        ],
     ),
     # musllinux_1_2 images
     PyPAImage("musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l"]),
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index d977c90b7..7dbc37982 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,49 +1,51 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.14-5
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.14-5
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.14-5
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.14-5
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.14-5
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.14-5
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.14-5
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.14-5
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.14-5
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.14-5
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.14-5
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.14-5
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.12-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.12-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.12-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.14-5
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.14-5
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.14-5
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.12-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.12-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.14-5
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.14-5
 
 [riscv64]
 
diff --git a/docs/options.md b/docs/options.md
index d545adc19..fcc64b878 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1029,7 +1029,6 @@ The available options are:
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
 
 For `manylinux-*-image`, except `manylinux-armv7l-image`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
-`manylinux_2_34` is not supported for `i686` architecture.
 
 For `manylinux-armv7l-image`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
 
diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py
index 8754b363b..c39ca5ac6 100644
--- a/test/test_manylinuxXXXX_only.py
+++ b/test/test_manylinuxXXXX_only.py
@@ -64,8 +64,6 @@
 def test(manylinux_image, tmp_path):
     if utils.get_platform() != "linux":
         pytest.skip("the container image test is only relevant to the linux build")
-    elif manylinux_image == "manylinux_2_34" and platform.machine() == "i686":
-        pytest.skip(f"{manylinux_image} doesn't exist for i686 architecture")
 
     project_dir = tmp_path / "project"
     project_with_manylinux_symbols.generate(project_dir)
@@ -85,9 +83,6 @@ def test(manylinux_image, tmp_path):
         "CIBW_MANYLINUX_PYPY_AARCH64_IMAGE": manylinux_image,
         "CIBW_MANYLINUX_PYPY_I686_IMAGE": manylinux_image,
     }
-    if manylinux_image == "manylinux_2_34" and platform.machine() == "x86_64":
-        # We don't have a manylinux_2_34+ image for i686
-        add_env["CIBW_ARCHS"] = "x86_64"
     if platform.machine() == "aarch64":
         # We just have a manylinux_2_31 image for armv7l
         add_env["CIBW_ARCHS"] = "aarch64"
@@ -104,10 +99,6 @@ def test(manylinux_image, tmp_path):
         musllinux_versions=[],
     )
 
-    if manylinux_image == "manylinux_2_34" and platform.machine() == "x86_64":
-        # We don't have a manylinux_2_34+ image for i686
-        expected_wheels = [w for w in expected_wheels if "i686" not in w]
-
     if platform.machine() == "aarch64":
         # We just have a manylinux_2_31 image for armv7l
         expected_wheels = [w for w in expected_wheels if "armv7l" not in w]

From fbf37256a5b42e82bfe9b7c9672a200e0e1d65f9 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sat, 19 Jul 2025 16:38:05 -0700
Subject: [PATCH 161/193] feat: include free-threaded 3.14 by default (#2503)

This is no longer experimental in 3.13 beta 3, so we don't need an enable for it anymore. Someone can skip expliclity with `'cp31?t-*'` if they really don't want free-threaded wheels.

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/selector.py | 2 +-
 docs/options.md          | 9 +++++----
 test/utils.py            | 3 +--
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index f0bce928a..0b986e696 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -85,7 +85,7 @@ def __call__(self, build_id: str) -> bool:
                 return False
 
         # filter out groups that are not enabled
-        if EnableGroup.CPythonFreeThreading not in self.enable and fnmatch(build_id, "cp3??t-*"):
+        if EnableGroup.CPythonFreeThreading not in self.enable and fnmatch(build_id, "cp313t-*"):
             return False
         if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp314*"):
             return False
diff --git a/docs/options.md b/docs/options.md
index fcc64b878..e35b9fb5b 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -348,9 +348,10 @@ values are:
 - `cpython-freethreading`: [PEP 703](https://www.python.org/dev/peps/pep-0703)
   introduced variants of CPython that can be built without the Global
   Interpreter Lock (GIL).  Those variants are also known as free-threaded /
-  no-gil. This will enable building these wheels while they are experimental.
-  The build identifiers for those variants have a `t` suffix in their
-  `python_tag` (e.g. `cp313t-manylinux_x86_64`).
+  no-gil. This will enable building these wheels for 3.13, which was considered
+  experimental.  The build identifiers for those variants have a `t` suffix in
+  their `python_tag` (e.g. `cp313t-manylinux_x86_64`). Starting in 3.14,
+  free-threaded builds are no longer experimental, so this enable is not needed.
 - `pypy`: Enable PyPy.
 - `pypy-eol`: Enable PyPy versions that have passed end of life (if still available).
 - `cpython-experimental-riscv64`: Enable experimental riscv64 builds. Those builds
@@ -371,7 +372,7 @@ values are:
     that version of Python will become available without this flag.
 
 !!! note
-    Free threading is experimental: [What’s New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
+    Free threading was experimental in 3.13: [What’s New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
 
 Default: empty.
 
diff --git a/test/utils.py b/test/utils.py
index ccd467aa9..d23d92c6e 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -280,8 +280,7 @@ def _expected_wheels(
 
         if EnableGroup.CPythonPrerelease in enable_groups:
             python_abi_tags.append("cp314-cp314")
-            if EnableGroup.CPythonFreeThreading in enable_groups:
-                python_abi_tags.append("cp314-cp314t")
+            python_abi_tags.append("cp314-cp314t")
 
         if EnableGroup.PyPyEoL in enable_groups:
             python_abi_tags += [

From a8bff94dbb5f3a4a914e29cf8353c2f6f1b9ab8b Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 21 Jul 2025 14:16:55 +0200
Subject: [PATCH 162/193] [Bot] Update dependencies (#2504)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 cibuildwheel/resources/build-platforms.toml   |  6 +-
 .../resources/constraints-pyodide312.txt      |  4 +-
 .../resources/constraints-pyodide313.txt      |  4 +-
 .../resources/constraints-python310.txt       |  4 +-
 .../resources/constraints-python311.txt       |  4 +-
 .../resources/constraints-python312.txt       |  4 +-
 .../resources/constraints-python313.txt       |  4 +-
 .../resources/constraints-python314.txt       |  4 +-
 .../resources/constraints-python38.txt        |  4 +-
 .../resources/constraints-python39.txt        |  4 +-
 cibuildwheel/resources/constraints.txt        |  4 +-
 cibuildwheel/resources/nodejs.toml            |  4 +-
 .../resources/pinned_docker_images.cfg        | 62 +++++++++----------
 cibuildwheel/resources/virtualenv.toml        |  2 +-
 docs/working-examples.md                      |  8 +--
 15 files changed, 61 insertions(+), 61 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index cbef21ca1..50904fdc6 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -179,8 +179,8 @@ python_configurations = [
   { identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
   { identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_x86_64.tar.bz2" },
   { identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_arm64.tar.bz2" },
-  { identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-amd64.tar.gz" },
-  { identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-macos-aarch64.tar.gz" },
+  { identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-amd64.tar.gz" },
+  { identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-aarch64.tar.gz" },
 ]
 
 [windows]
@@ -215,7 +215,7 @@ python_configurations = [
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
   { identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.20-win64.zip" },
-  { identifier = "gp311_242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.1/graalpy-24.2.1-windows-amd64.zip" },
+  { identifier = "gp311_242-win_amd64", version = "3.11", arch = "64", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-windows-amd64.zip" },
 ]
 
 [pyodide]
diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 46e762e70..88a0628be 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -21,7 +21,7 @@ click==8.1.8
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   typer
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -107,7 +107,7 @@ unearth==0.17.5
     # via pyodide-build
 urllib3==2.5.0
     # via requests
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via
     #   build
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index 5d2649499..b713eee58 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -21,7 +21,7 @@ click==8.1.8
     # via
     #   -r .nox/update_constraints/tmp/constraints-pyodide.in
     #   typer
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -106,7 +106,7 @@ unearth==0.17.5
     # via pyodide-build
 urllib3==2.5.0
     # via requests
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via
     #   build
     #   pyodide-build
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index c02574796..f9ce5861c 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.14.1
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.23.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index f13abf533..ebe71225a 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.14.1
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
index f13abf533..ebe71225a 100644
--- a/cibuildwheel/resources/constraints-python312.txt
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.14.1
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python313.txt b/cibuildwheel/resources/constraints-python313.txt
index f13abf533..ebe71225a 100644
--- a/cibuildwheel/resources/constraints-python313.txt
+++ b/cibuildwheel/resources/constraints-python313.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.14.1
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python314.txt b/cibuildwheel/resources/constraints-python314.txt
index f13abf533..ebe71225a 100644
--- a/cibuildwheel/resources/constraints-python314.txt
+++ b/cibuildwheel/resources/constraints-python314.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.14.1
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 2c40131f4..d0b9afa23 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.12.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.16.1
     # via virtualenv
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.13.2
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.20.2
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index c02574796..f9ce5861c 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -28,7 +28,7 @@ tomli==2.2.1
     # via build
 typing-extensions==4.14.1
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
 zipp==3.23.0
     # via importlib-metadata
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index f13abf533..ebe71225a 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -6,7 +6,7 @@ build==1.2.2.post1
     # via -r cibuildwheel/resources/constraints.in
 delocate==0.13.0
     # via -r cibuildwheel/resources/constraints.in
-distlib==0.3.9
+distlib==0.4.0
     # via virtualenv
 filelock==3.18.0
     # via virtualenv
@@ -24,5 +24,5 @@ pyproject-hooks==1.2.0
     # via build
 typing-extensions==4.14.1
     # via delocate
-virtualenv==20.31.2
+virtualenv==20.32.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/nodejs.toml b/cibuildwheel/resources/nodejs.toml
index 01ca7aba9..ee8bf6164 100644
--- a/cibuildwheel/resources/nodejs.toml
+++ b/cibuildwheel/resources/nodejs.toml
@@ -1,3 +1,3 @@
 url = "https://nodejs.org/dist/"
-v22 = "v22.17.0"
-v20 = "v20.19.3"
+v22 = "v22.17.1"
+v20 = "v20.19.4"
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 7dbc37982..26b0704bf 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,51 +1,51 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.14-5
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.20-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.20-2
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.14-5
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.20-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.20-2
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.14-5
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.20-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.20-2
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.14-5
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.20-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.20-2
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.14-5
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.20-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.20-2
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.20-2
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.20-2
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.14-5
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.14-5
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.14-5
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.20-2
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.20-2
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.20-2
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.14-5
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.14-5
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.20-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.20-2
 
 [riscv64]
 
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index 9e5d08c24..5eff9df47 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1 +1 @@
-default = { version = "20.31.2", url = "https://github.com/pypa/get-virtualenv/blob/20.31.2/public/virtualenv.pyz?raw=true" }
+default = { version = "20.32.0", url = "https://github.com/pypa/get-virtualenv/blob/20.32.0/public/virtualenv.pyz?raw=true" }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 4f3caaa17..4d8012aad 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -76,13 +76,13 @@ title: Working examples
 | [KDEpy][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python |
 | [dd-trace-py][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
 | [tgcalls][]                       | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. |
-| [pybind11 python_example][]       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
 | [python-rapidjson][]              | ![travisci icon][] ![gitlab icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson |
+| [pybind11 python_example][]       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
 | [sourmash][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
 | [abess][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
 | [python-snappy][]                 | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library |
-| [jq.py][]                         | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
 | [cyvcf2][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
+| [jq.py][]                         | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
 | [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [Tokenizer][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
 | [Confluent client for Kafka][]    | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
@@ -186,13 +186,13 @@ title: Working examples
 [KDEpy]: https://github.com/tommyod/KDEpy
 [dd-trace-py]: https://github.com/DataDog/dd-trace-py
 [tgcalls]: https://github.com/MarshalX/tgcalls
-[pybind11 python_example]: https://github.com/pybind/python_example
 [python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson
+[pybind11 python_example]: https://github.com/pybind/python_example
 [sourmash]: https://github.com/sourmash-bio/sourmash
 [abess]: https://github.com/abess-team/abess
 [python-snappy]: https://github.com/intake/python-snappy
-[jq.py]: https://github.com/mwilliamson/jq.py
 [cyvcf2]: https://github.com/brentp/cyvcf2
+[jq.py]: https://github.com/mwilliamson/jq.py
 [matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [Tokenizer]: https://github.com/OpenNMT/Tokenizer
 [Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python

From e2e24882d8422e974295b1b9079d4ce80a5098a4 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Wed, 23 Jul 2025 13:28:36 +0200
Subject: [PATCH 163/193] feat: add default riscv64 images (#2506)

---
 bin/update_docker.py                            |  9 +++++----
 cibuildwheel/resources/defaults.toml            |  4 ++--
 cibuildwheel/resources/pinned_docker_images.cfg |  2 ++
 docs/options.md                                 |  8 +++++---
 test/test_emulation.py                          | 13 +++----------
 test/utils.py                                   |  2 +-
 6 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/bin/update_docker.py b/bin/update_docker.py
index ad43307fa..7c8c42c7b 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -71,8 +71,12 @@ def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None
             "pypy_aarch64",
         ],
     ),
+    # manylinux_2_39 images
+    PyPAImage("manylinux_2_39", ["riscv64"]),
     # musllinux_1_2 images
-    PyPAImage("musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l"]),
+    PyPAImage(
+        "musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l", "riscv64"]
+    ),
 ]
 
 config = configparser.ConfigParser()
@@ -136,8 +140,5 @@ def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None
             suffix = f"_{platform.removeprefix('pypy_')}"
         config[platform][image.manylinux_version] = f"{image.image_name}{suffix}:{tag_name}"
 
-if not config.has_section("riscv64"):
-    config["riscv64"] = {}
-
 with open(RESOURCES / "pinned_docker_images.cfg", "w") as f:
     config.write(f)
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 6a57f2d03..9901ec895 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -36,7 +36,7 @@ manylinux-aarch64-image = "manylinux_2_28"
 manylinux-ppc64le-image = "manylinux_2_28"
 manylinux-s390x-image = "manylinux_2_28"
 manylinux-armv7l-image = "manylinux_2_31"
-manylinux-riscv64-image = "ghcr.io/pypa/cibuildwheel/no_default_image:please_use_override"
+manylinux-riscv64-image = "manylinux_2_39"
 manylinux-pypy_x86_64-image = "manylinux_2_28"
 manylinux-pypy_i686-image = "manylinux_2_28"
 manylinux-pypy_aarch64-image = "manylinux_2_28"
@@ -47,7 +47,7 @@ musllinux-aarch64-image = "musllinux_1_2"
 musllinux-ppc64le-image = "musllinux_1_2"
 musllinux-s390x-image = "musllinux_1_2"
 musllinux-armv7l-image = "musllinux_1_2"
-musllinux-riscv64-image = "ghcr.io/pypa/cibuildwheel/no_default_image:please_use_override"
+musllinux-riscv64-image = "musllinux_1_2"
 
 
 [tool.cibuildwheel.linux]
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 26b0704bf..e4bab3e8a 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -48,4 +48,6 @@ manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.20-2
 musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.20-2
 
 [riscv64]
+manylinux_2_39 = quay.io/pypa/manylinux_2_39_riscv64:2025.07.20-2
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_riscv64:2025.07.20-2
 
diff --git a/docs/options.md b/docs/options.md
index e35b9fb5b..b4fdf6361 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -1016,7 +1016,7 @@ The available options are:
 | `manylinux-ppc64le-image`      | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
 | `manylinux-s390x-image`        | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x)   |
 | `manylinux-armv7l-image`       | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l)  |
-| `manylinux-riscv64-image`      | No default                                                      |
+| `manylinux-riscv64-image`      | [`manylinux_2_39`](https://quay.io/pypa/manylinux_2_39_riscv64) |
 | `manylinux-pypy_aarch64-image` | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
 | `manylinux-pypy_i686-image`    | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_i686)    |
 | `musllinux_x86_64-image`       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64)    |
@@ -1025,14 +1025,16 @@ The available options are:
 | `musllinux-ppc64le-image`      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le)   |
 | `musllinux-s390x-image`        | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x)     |
 | `musllinux-armv7l-image`       | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l)    |
-| `musllinux-riscv64-image`      | No default                                                      |
+| `musllinux-riscv64-image`      | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_riscv64)   |
 
 Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
 
-For `manylinux-*-image`, except `manylinux-armv7l-image`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
+For `manylinux-*-image`, except `manylinux-armv7l-image` and `manylinux-riscv64-image`, the value of this option can either be set to `manylinux2014`, `manylinux_2_28` or `manylinux_2_34` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name.
 
 For `manylinux-armv7l-image`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
 
+For `manylinux-riscv64-image`, the value of this option can either be set to `manylinux_2_39` or a custom image. Support is experimental for now. The `manylinux_2_39` value is only available for `riscv64`.
+
 For `musllinux-*-image`, the value of this option can either be set to `musllinux_1_2` or a custom image.
 
 If this option is blank, it will fall though to the next available definition (environment variable -> pyproject.toml -> default).
diff --git a/test/test_emulation.py b/test/test_emulation.py
index 4a749cfa3..b0dc4c0d6 100644
--- a/test/test_emulation.py
+++ b/test/test_emulation.py
@@ -34,21 +34,14 @@ def test(tmp_path, request):
             "CIBW_TEST_REQUIRES": "pytest",
             "CIBW_TEST_COMMAND": "pytest {project}/test",
             "CIBW_ARCHS": archs,
-            # TODO remove me once proper support is added
-            "CIBW_MANYLINUX_RISCV64_IMAGE": "ghcr.io/mayeut/manylinux_2_35:2025.05.11-1",
-            "CIBW_SKIP": "*-musllinux_riscv64",
         },
     )
 
     # also check that we got the right wheels
-    expected_wheels = list(
-        itertools.chain.from_iterable(
-            utils.expected_wheels("spam", "0.1.0", machine_arch=arch, single_arch=True)
-            for arch in archs.split(" ")
-        )
+    expected_wheels = itertools.chain.from_iterable(
+        utils.expected_wheels("spam", "0.1.0", machine_arch=arch, single_arch=True)
+        for arch in archs.split(" ")
     )
-    # TODO remove me once proper support is added
-    expected_wheels = [wheel for wheel in expected_wheels if "musllinux_1_2_riscv64" not in wheel]
     assert set(actual_wheels) == set(expected_wheels)
 
 
diff --git a/test/utils.py b/test/utils.py
index d23d92c6e..c4310d379 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -254,7 +254,7 @@ def _expected_wheels(
             "armv7l": ["manylinux2014", "manylinux_2_17", "manylinux_2_31"],
             "i686": ["manylinux1", "manylinux_2_28", "manylinux_2_5"],
             "x86_64": ["manylinux1", "manylinux_2_28", "manylinux_2_5"],
-            "riscv64": ["manylinux_2_31", "manylinux_2_35"],
+            "riscv64": ["manylinux_2_31", "manylinux_2_39"],
         }.get(machine_arch, ["manylinux2014", "manylinux_2_17", "manylinux_2_28"])
 
     if musllinux_versions is None:

From 0f487ee2cb00876d95290da49d04208c91237857 Mon Sep 17 00:00:00 2001
From: Malcolm Smith <smith@chaquo.com>
Date: Wed, 23 Jul 2025 22:18:33 +0100
Subject: [PATCH 164/193] feat: add support for building Android wheels (#2349)

* Add Android to resource files

* Add Android to miscellaneous places

* Add Android documentation

* Docs cleanups

* Add Android platform module; implement top-level structure and target Python installation

* Implement setup_env and build_wheel

* lru-dict build working

* Alter prefix in sysconfigdata file; fix various issues with FLAGS variables

* Implement Android testing

* Add type annotations to _cross_venv

* Revert Python 3.8 to pip 25.0.1

* Make test-sources required on Android

* Add Android integration tests

* Test cleanups

* Add test of all available Python versions

* Update test-sources and test-command behavior to match iOS

* Documentation cleanups

* Replace Builder class with a set of global functions

* Rename "env" to "build_env"

* Remove Chaquopy repository from default pip command line

* Move native_platform to platforms module

* Fix parse_config_settings

Co-authored-by: Joe Rickerby <joerick@mac.com>

* Add unit tests for parse_config_settings and arch_synonym

* Make `shell_prepared` arguments keyword-only, and add tests for the commands that use it

* Replace `importlib.util.spec_from_file_location` with `runpy.run_path`

* Use python-build-standalone

* Update Android Python

* Enable KVM in Linux CI

* Move KVM code to test_android.py

* Use Java 17 on Azure

* Install emulator if necessary before running -accel-check

* Free up additional disk space on Linux runners

* Add sudo

* Skip emulator tests on CI platforms that don't support it

* Download Android Python from Maven Central

* Free up more disk space on Linux runners

* fix: minor fixups

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Set sysconfig._BASE_PREFIX to support sysconfig.get_path("include")

* Get ANDROID_API_LEVEL from the build environment, not cibuildwheel's own environment

* Correct relative path of test-sources

* Pass a CMake toolchain file to the build

* Add "repair" step which adds libc++ to the wheel when necessary

* Add missing needs_emulator decorator

* Provide useful error message if ANDROID_HOME is not set

* Remove use of HOST environment variable

* Update to Python 3.15.5

* Fix PyLint warnings, clarify comment

* Group common arguments into a dataclass

* Handle environment variables containing newlines

* Discourage the use of `pytest` test commands without `python -m`

* Use single quotes in user-visible messages

* Improve testing documentation

* Pass wheel filename to `log.build_end`

* In GitHub Actions example, skip Android tests on macOS

* Correct relative paths in `patchelf --set-rpath`

* Clarify `test-sources` docs

* Update to Python 3.13.5+20250722.214220

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .github/workflows/test.yml                    |  14 +-
 .pre-commit-config.yaml                       |   4 +-
 README.md                                     |  60 +-
 azure-pipelines.yml                           |   5 +
 bin/generate_schema.py                        |   1 +
 bin/update_pythons.py                         |  60 +-
 cibuildwheel/__main__.py                      |  28 +-
 cibuildwheel/architecture.py                  |  32 +-
 cibuildwheel/frontend.py                      |  16 +
 cibuildwheel/logger.py                        |   2 +
 cibuildwheel/options.py                       |   5 +-
 cibuildwheel/platforms/__init__.py            |  21 +-
 cibuildwheel/platforms/android.py             | 644 ++++++++++++++++++
 cibuildwheel/platforms/ios.py                 |   3 +-
 cibuildwheel/platforms/linux.py               |   4 +-
 cibuildwheel/platforms/macos.py               |   4 +-
 cibuildwheel/platforms/pyodide.py             |   4 +-
 cibuildwheel/platforms/windows.py             |   4 +-
 cibuildwheel/resources/_cross_venv.pth        |   1 +
 cibuildwheel/resources/_cross_venv.py         |  83 +++
 cibuildwheel/resources/build-platforms.toml   |   6 +
 .../resources/cibuildwheel.schema.json        |  53 +-
 cibuildwheel/resources/defaults.toml          |   2 +
 .../resources/testing_temp_dir_file.py        |   4 +-
 cibuildwheel/typing.py                        |   2 +-
 cibuildwheel/util/packaging.py                |   6 +-
 docs/changelog.md                             |   6 +-
 docs/ci-services.md                           |   2 +-
 docs/options.md                               |  97 +--
 docs/platforms.md                             |  75 +-
 examples/github-deploy.yml                    |  19 +
 pyproject.toml                                |   5 +-
 test/test_android.py                          | 383 +++++++++++
 test/test_projects/c.py                       |   2 +-
 test/utils.py                                 |  11 +-
 unit_test/architecture_test.py                |  19 +-
 unit_test/main_tests/main_options_test.py     |  17 +-
 unit_test/options_test.py                     |   4 +-
 38 files changed, 1548 insertions(+), 160 deletions(-)
 create mode 100644 cibuildwheel/platforms/android.py
 create mode 100644 cibuildwheel/resources/_cross_venv.pth
 create mode 100644 cibuildwheel/resources/_cross_venv.py
 create mode 100644 test/test_android.py

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 229bce922..c2ec74b99 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -70,13 +70,23 @@ jobs:
 
     - uses: astral-sh/setup-uv@v6
 
-    # free some space to prevent reaching GHA disk space limits
-    - name: Clean docker images
+    - name: Free up disk space
       if: runner.os == 'Linux'
       run: |
         docker system prune -a -f
+        sudo rm -rf $ANDROID_HOME/ndk/{26,28}.* /opt/hostedtoolcache/CodeQL \
+          /usr/local/lib/node_modules /usr/local/share/chromium \
+          /usr/local/share/powershell
         df -h
 
+    # https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
+    - name: Enable KVM for Android emulator
+      if: runner.os == 'Linux' && runner.arch == 'X64'
+      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
+
     # for oci_container unit tests
     - name: Set up QEMU
       if: runner.os == 'Linux'
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0bdea9190..cc9411891 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -26,13 +26,16 @@ repos:
   - id: mypy
     name: mypy 3.11 on cibuildwheel/
     args: ["--python-version=3.11"]
+    exclude: ^cibuildwheel/resources/_cross_venv.py$  # Requires Python 3.13 or later
     additional_dependencies: &mypy-dependencies
       - bracex
+      - build
       - dependency-groups>=1.2
       - humanize
       - nox>=2025.2.9
       - orjson
       - packaging
+      - pyelftools
       - pygithub
       - pytest
       - rich
@@ -47,7 +50,6 @@ repos:
       - validate-pyproject
   - id: mypy
     name: mypy 3.13
-    exclude: ^cibuildwheel/resources/.*py$
     args: ["--python-version=3.13"]
     additional_dependencies: *mypy-dependencies
 
diff --git a/README.md b/README.md
index 8c69f9ae0..6ac4156fc 100644
--- a/README.md
+++ b/README.md
@@ -23,20 +23,20 @@ What does it do?
 
 While cibuildwheel itself requires a recent Python version to run (we support the last three releases), it can target the following versions to build wheels:
 
-|                    | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | iOS | Pyodide |
-|--------------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|-----|
-| CPython 3.8        | ✅ | ✅  | ✅  | ✅  | N/A | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.9        | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅ | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.10       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.11       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A |
-| CPython 3.12       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | ✅⁴ |
-| CPython 3.13³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
-| CPython 3.14³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | N/A |
-| PyPy 3.8 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| PyPy 3.9 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| PyPy 3.10 v7.3     | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| PyPy 3.11 v7.3     | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
-| GraalPy 3.11 v24.2 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | N/A  | ✅¹ | N/A | N/A | N/A | N/A | N/A |
+|                    | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | Android | iOS | Pyodide |
+|--------------------|----|-----|----|-----|-----|----|-----|----|-----|-----|---|-----|-----|-----|
+| CPython 3.8        | ✅ | ✅  | ✅  | ✅  | N/A | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A | N/A |
+| CPython 3.9        | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅ | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A | N/A |
+| CPython 3.10       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A | N/A |
+| CPython 3.11       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A | N/A |
+| CPython 3.12       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | N/A | ✅⁴ |
+| CPython 3.13³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | ✅ | N/A |
+| CPython 3.14³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | N/A | N/A |
+| PyPy 3.8 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.9 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.10 v7.3     | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
+| PyPy 3.11 v7.3     | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
+| GraalPy 3.11 v24.2 | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | N/A  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
 
 <sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br>
 <sup>² Windows arm64 support is experimental.</sup><br>
@@ -56,19 +56,19 @@ Usage
 
 `cibuildwheel` runs inside a CI service. Supported platforms depend on which service you're using:
 
-|                 | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS |
-|-----------------|-------|-------|---------|-----------|-----------|-------------|-----|
-| GitHub Actions  | ✅    | ✅    | ✅       | ✅        | ✅        | ✅          | ✅³  |
-| Azure Pipelines | ✅    | ✅    | ✅       |           | ✅        | ✅²         | ✅³  |
-| Travis CI       | ✅    |       | ✅      | ✅        |           |             |     |
-| CircleCI        | ✅    | ✅    |         | ✅        | ✅        |             | ✅³  |
-| Gitlab CI       | ✅    | ✅    | ✅      | ✅¹       | ✅        |             | ✅³  |
-| Cirrus CI       | ✅    | ✅    | ✅      | ✅        | ✅        |             |      |
+|                 | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | Android | iOS |
+|-----------------|-------|-------|---------|-----------|-----------|-------------|---------|-----|
+| GitHub Actions  | ✅    | ✅    | ✅       | ✅        | ✅        | ✅²         | ✅⁴      | ✅³  |
+| Azure Pipelines | ✅    | ✅    | ✅       |           | ✅        | ✅²         | ✅⁴      | ✅³  |
+| Travis CI       | ✅    |       | ✅      | ✅        |           |             | ✅⁴      |     |
+| CircleCI        | ✅    | ✅    |         | ✅        | ✅        |             | ✅⁴      | ✅³  |
+| Gitlab CI       | ✅    | ✅    | ✅      | ✅¹       | ✅        |             | ✅⁴      | ✅³  |
+| Cirrus CI       | ✅    | ✅    | ✅      | ✅        | ✅        |             | ✅⁴      |      |
 
 <sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#emulation), distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.</sup><br>
 <sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
-<sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture.</sup>
-
+<sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture.</sup><br>
+<sup>⁴ Building for Android requires the runner to be Linux x86_64, macOS ARM64 or macOS x86_64. Testing has [additional requirements](https://cibuildwheel.pypa.io/en/stable/platforms/#android).</sup><br>
 <!--intro-end-->
 
 Example setup
@@ -151,7 +151,7 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
 |  | [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) | Specify the Pyodide version to use for `pyodide` platform builds |
 | **Testing** | [`test-command`](https://cibuildwheel.pypa.io/en/stable/options/#test-command) | The command to test each built wheel |
 |  | [`before-test`](https://cibuildwheel.pypa.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
-|  | [`test-sources`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources) | Files and folders from the source tree that are copied into an isolated tree before running the tests |
+|  | [`test-sources`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources) | Paths that are copied into the working directory of the tests |
 |  | [`test-requires`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
 |  | [`test-extras`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras) | Install your wheel for testing using `extras_require` |
 |  | [`test-groups`](https://cibuildwheel.pypa.io/en/stable/options/#test-groups) | Specify test dependencies from your project's `dependency-groups` |
@@ -162,7 +162,7 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
 |  | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build |
 
 
-<!--[[[end]]] (sum: TWqEGGMOnt) -->
+<!--[[[end]]] (sum: FxE3nIgFiY) -->
 
 These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/).
 
@@ -245,10 +245,10 @@ See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) f
 - ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b2. (#2390)
 
     _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
-- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), and changes the working directory for tests. (#2062, #2284, #2437)
 
-    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into the temporary directory we run from. This is required for iOS builds, but also useful for other platforms, as it allows you to avoid placeholders.
-    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory. (#2420)
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), which copies files and folders into the temporary working directory we run tests from. (#2062, #2284, #2420, #2437)
+
+    This is particularly important for iOS builds, which do not support placeholders in the `test-command`, but can also be useful for other platforms.
 
 - ✨ Adds [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) inline syntax (#2122)
 - ✨ Improves support for Pyodide builds and adds the experimental [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
@@ -292,7 +292,7 @@ _15 March 2025_
 - ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
 - 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
 
-<!-- [[[end]]] (sum: QKx9Hx5znR) -->
+<!-- [[[end]]] (sum: fBN/s9Yq6D) -->
 
 ---
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 23d6928e0..67dfac458 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -23,6 +23,11 @@ jobs:
     - task: UsePythonVersion@0
       inputs:
         versionSpec: '3.11'
+    - task: JavaToolInstaller@0
+      inputs:
+        versionSpec: '17'
+        jdkArchitectureOption: 'x64'
+        jdkSourceOption: 'PreInstalled'
     - bash: |
         docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
         python -m pip install -U pip
diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index cc4e8730f..df0bfdf01 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -348,6 +348,7 @@ def as_object(d: dict[str, Any]) -> dict[str, Any]:
     "windows": as_object(not_linux),
     "macos": as_object(not_linux),
     "pyodide": as_object(not_linux),
+    "android": as_object(not_linux),
     "ios": as_object(not_linux),
 }
 
diff --git a/bin/update_pythons.py b/bin/update_pythons.py
index d1058f452..20a99861d 100755
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -10,6 +10,7 @@
 from collections.abc import Mapping, MutableMapping
 from pathlib import Path
 from typing import Any, Final, Literal, TypedDict
+from xml.etree import ElementTree as ET
 
 import click
 import requests
@@ -20,6 +21,7 @@
 from rich.syntax import Syntax
 
 from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info
+from cibuildwheel.platforms.android import android_triplet
 
 log = logging.getLogger("cibw")
 
@@ -57,6 +59,12 @@ class ConfigApple(TypedDict):
     url: str
 
 
+class ConfigAndroid(TypedDict):
+    identifier: str
+    version: str
+    url: str
+
+
 class ConfigPyodide(TypedDict):
     identifier: str
     version: str
@@ -64,7 +72,7 @@ class ConfigPyodide(TypedDict):
     node_version: str
 
 
-AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple | ConfigPyodide
+AnyConfig = ConfigWinCP | ConfigWinPP | ConfigWinGP | ConfigApple | ConfigAndroid | ConfigPyodide
 
 
 # The following set of "Versions" classes allow the initial call to the APIs to
@@ -320,6 +328,39 @@ def update_version_macos(
         return None
 
 
+class AndroidVersions:
+    # This should be replaced with official python.org downloads once they're available.
+    MAVEN_URL = "https://repo.maven.apache.org/maven2/com/chaquo/python/python"
+
+    def __init__(self) -> None:
+        response = requests.get(f"{self.MAVEN_URL}/maven-metadata.xml")
+        response.raise_for_status()
+        root = ET.fromstring(response.text)
+
+        self.versions: list[Version] = []
+        for version_elem in root.findall("./versioning/versions/version"):
+            version_str = version_elem.text
+            assert isinstance(version_str, str), version_str
+            self.versions.append(Version(version_str))
+
+    def update_version_android(
+        self, identifier: str, version: Version, spec: Specifier
+    ) -> ConfigAndroid | None:
+        sorted_versions = sorted(spec.filter(self.versions), reverse=True)
+
+        # Return a config using the highest version for the given specifier.
+        if sorted_versions:
+            max_version = sorted_versions[0]
+            triplet = android_triplet(identifier)
+            return ConfigAndroid(
+                identifier=identifier,
+                version=str(version),
+                url=f"{self.MAVEN_URL}/{max_version}/python-{max_version}-{triplet}.tar.gz",
+            )
+        else:
+            return None
+
+
 class CPythonIOSVersions:
     def __init__(self) -> None:
         response = requests.get(
@@ -413,6 +454,7 @@ def __init__(self) -> None:
         self.macos_pypy = PyPyVersions("64")
         self.macos_pypy_arm64 = PyPyVersions("ARM64")
 
+        self.android = AndroidVersions()
         self.ios_cpython = CPythonIOSVersions()
 
         self.graalpy = GraalPyVersions()
@@ -455,6 +497,8 @@ def update_config(self, config: MutableMapping[str, str]) -> None:
             config_update = self.windows_t_arm64.update_version_windows(spec)
         elif "win_arm64" in identifier and identifier.startswith("cp"):
             config_update = self.windows_arm64.update_version_windows(spec)
+        elif "android" in identifier:
+            config_update = self.android.update_version_android(identifier, version, spec)
         elif "ios" in identifier:
             config_update = self.ios_cpython.update_version_ios(identifier, version)
         elif "pyodide" in identifier:
@@ -490,17 +534,9 @@ def update_pythons(force: bool, level: str) -> None:
     with toml_file_path.open("rb") as f:
         configs = tomllib.load(f)
 
-    for config in configs["windows"]["python_configurations"]:
-        all_versions.update_config(config)
-
-    for config in configs["macos"]["python_configurations"]:
-        all_versions.update_config(config)
-
-    for config in configs["ios"]["python_configurations"]:
-        all_versions.update_config(config)
-
-    for config in configs["pyodide"]["python_configurations"]:
-        all_versions.update_config(config)
+    for platform in ["windows", "macos", "android", "ios", "pyodide"]:
+        for config in configs[platform]["python_configurations"]:
+            all_versions.update_config(config)
 
     result_toml = dump_python_configurations(configs)
 
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index 60849857a..cdec170d7 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -21,7 +21,7 @@
 from cibuildwheel.ci import CIProvider, detect_ci_provider, fix_ansi_codes_for_github_actions
 from cibuildwheel.logger import log
 from cibuildwheel.options import CommandLineArguments, Options, compute_options
-from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers
+from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers, native_platform
 from cibuildwheel.selector import BuildSelector, EnableGroup, selector_matches
 from cibuildwheel.typing import PLATFORMS, PlatformName
 from cibuildwheel.util.file import CIBW_CACHE_PATH
@@ -94,14 +94,14 @@ def main_inner(global_options: GlobalOptions) -> None:
 
     parser.add_argument(
         "--platform",
-        choices=["auto", "linux", "macos", "windows", "pyodide", "ios"],
+        choices=["auto", "linux", "macos", "windows", "pyodide", "android", "ios"],
         default=None,
         help="""
             Platform to build for. Use this option to override the auto-detected
             platform. Specifying "macos" or "windows" only works on that
             operating system. "linux" works on any desktop OS, as long as
-            Docker/Podman is installed. "pyodide" only works on linux and macOS.
-            "ios" only work on macOS. Default: auto.
+            Docker/Podman is installed. "pyodide" and "android" only work on
+            Linux and macOS. "ios" only works on macOS. Default: auto.
         """,
     )
 
@@ -244,28 +244,14 @@ def _compute_platform_only(only: str) -> PlatformName:
         return "windows"
     if "pyodide_" in only:
         return "pyodide"
+    if "android_" in only:
+        return "android"
     if "ios_" in only:
         return "ios"
     msg = f"Invalid --only='{only}', must be a build selector with a known platform"
     raise errors.ConfigurationError(msg)
 
 
-def _compute_platform_auto() -> PlatformName:
-    if sys.platform.startswith("linux"):
-        return "linux"
-    elif sys.platform == "darwin":
-        return "macos"
-    elif sys.platform == "win32":
-        return "windows"
-    else:
-        msg = (
-            'Unable to detect platform from "sys.platform". cibuildwheel doesn\'t '
-            "support building wheels for this platform. You might be able to build for a different "
-            "platform using the --platform argument. Check --help output for more information."
-        )
-        raise errors.ConfigurationError(msg)
-
-
 def _compute_platform(args: CommandLineArguments) -> PlatformName:
     platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "") or "auto"
 
@@ -285,7 +271,7 @@ def _compute_platform(args: CommandLineArguments) -> PlatformName:
     elif platform_option_value != "auto":
         return typing.cast(PlatformName, platform_option_value)
 
-    return _compute_platform_auto()
+    return native_platform()
 
 
 def build_in_directory(args: CommandLineArguments) -> None:
diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py
index 69c164b27..bf0af268f 100644
--- a/cibuildwheel/architecture.py
+++ b/cibuildwheel/architecture.py
@@ -17,16 +17,25 @@
     "macos": "macOS",
     "windows": "Windows",
     "pyodide": "Pyodide",
+    "android": "Android",
     "ios": "iOS",
 }
 
 ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
-    {"linux": "x86_64", "macos": "x86_64", "windows": "AMD64"},
+    {"linux": "x86_64", "macos": "x86_64", "windows": "AMD64", "android": "x86_64"},
     {"linux": "i686", "macos": None, "windows": "x86"},
-    {"linux": "aarch64", "macos": "arm64", "windows": "ARM64"},
+    {"linux": "aarch64", "macos": "arm64", "windows": "ARM64", "android": "arm64_v8a"},
 ]
 
 
+def arch_synonym(arch: str, from_platform: PlatformName, to_platform: PlatformName) -> str | None:
+    for arch_synonym_ in ARCH_SYNONYMS:
+        if arch == arch_synonym_.get(from_platform):
+            return arch_synonym_.get(to_platform, arch)
+
+    return arch
+
+
 def _check_aarch32_el0() -> bool:
     """Check if running armv7l natively on aarch64 is supported"""
     if not sys.platform.startswith("linux"):
@@ -42,7 +51,7 @@ def _check_aarch32_el0() -> bool:
 
 @typing.final
 class Architecture(StrEnum):
-    # mac/linux archs
+    # mac/linux/android archs
     x86_64 = auto()
 
     # linux archs
@@ -65,6 +74,9 @@ class Architecture(StrEnum):
     # WebAssembly
     wasm32 = auto()
 
+    # android archs
+    arm64_v8a = auto()
+
     # iOS "multiarch" architectures that include both
     # the CPU architecture and the ABI.
     arm64_iphoneos = auto()
@@ -123,15 +135,12 @@ def native_arch(platform: PlatformName) -> "Architecture | None":
         # we might need to rename the native arch to the machine we're running
         # on, as the same arch can have different names on different platforms
         if host_platform != platform:
-            for arch_synonym in ARCH_SYNONYMS:
-                if native_machine == arch_synonym.get(host_platform):
-                    synonym = arch_synonym[platform]
-
-                    if synonym is None:
-                        # can't build anything on this platform
-                        return None
+            synonym = arch_synonym(native_machine, host_platform, platform)
+            if synonym is None:
+                # can't build anything on this platform
+                return None
 
-                    native_architecture = Architecture(synonym)
+            native_architecture = Architecture(synonym)
 
         return native_architecture
 
@@ -166,6 +175,7 @@ def all_archs(platform: PlatformName) -> "set[Architecture]":
             "macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
             "windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
             "pyodide": {Architecture.wasm32},
+            "android": {Architecture.x86_64, Architecture.arm64_v8a},
             "ios": {
                 Architecture.x86_64_iphonesimulator,
                 Architecture.arm64_iphonesimulator,
diff --git a/cibuildwheel/frontend.py b/cibuildwheel/frontend.py
index 18157cdd0..7973354fe 100644
--- a/cibuildwheel/frontend.py
+++ b/cibuildwheel/frontend.py
@@ -58,6 +58,22 @@ def _split_config_settings(config_settings: str) -> list[str]:
     return [f"-C{setting}" for setting in config_settings_list]
 
 
+# Based on build.__main__.main.
+def parse_config_settings(config_settings_str: str) -> dict[str, str | list[str]]:
+    config_settings: dict[str, str | list[str]] = {}
+    for arg in shlex.split(config_settings_str):
+        setting, _, value = arg.partition("=")
+        existing_value = config_settings.get(setting)
+        if existing_value is None:
+            config_settings[setting] = value
+        elif isinstance(existing_value, str):
+            config_settings[setting] = [existing_value, value]
+        else:
+            existing_value.append(value)
+
+    return config_settings
+
+
 def get_build_frontend_extra_flags(
     build_frontend: BuildFrontendConfig, verbosity_level: int, config_settings: str
 ) -> list[str]:
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index 6ed3b761c..c2b90775d 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -50,6 +50,8 @@
     "macosx_universal2": "macOS Universal 2 - x86_64 and arm64",
     "macosx_arm64": "macOS arm64 - Apple Silicon",
     "pyodide_wasm32": "Pyodide",
+    "android_arm64_v8a": "Android arm64_v8a",
+    "android_x86_64": "Android x86_64",
     "ios_arm64_iphoneos": "iOS Device (ARM64)",
     "ios_arm64_iphonesimulator": "iOS Simulator (ARM64)",
     "ios_x86_64_iphonesimulator": "iOS Simulator (x86_64)",
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 6656df008..00deb0f8c 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -109,7 +109,7 @@ class BuildOptions:
     test_groups: list[str]
     test_environment: ParsedEnvironment
     build_verbosity: int
-    build_frontend: BuildFrontendConfig | None
+    build_frontend: BuildFrontendConfig
     config_settings: str
     container_engine: OCIContainerEngineConfig
     pyodide_version: str | None
@@ -772,9 +772,8 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
                 env_plat=False,
                 option_format=ShlexTableFormat(sep="; ", pair_sep=":", allow_merge=False),
             )
-            build_frontend: BuildFrontendConfig | None
             if not build_frontend_str or build_frontend_str == "default":
-                build_frontend = None
+                build_frontend = BuildFrontendConfig("build")
             else:
                 try:
                     build_frontend = BuildFrontendConfig.from_config_string(build_frontend_str)
diff --git a/cibuildwheel/platforms/__init__.py b/cibuildwheel/platforms/__init__.py
index a56fb9036..63c4c37d2 100644
--- a/cibuildwheel/platforms/__init__.py
+++ b/cibuildwheel/platforms/__init__.py
@@ -1,12 +1,14 @@
 from __future__ import annotations
 
+import sys
 from collections.abc import Sequence
 from pathlib import Path
 from typing import Final, Protocol
 
+from cibuildwheel import errors
 from cibuildwheel.architecture import Architecture
 from cibuildwheel.options import Options
-from cibuildwheel.platforms import ios, linux, macos, pyodide, windows
+from cibuildwheel.platforms import android, ios, linux, macos, pyodide, windows
 from cibuildwheel.selector import BuildSelector
 from cibuildwheel.typing import GenericPythonConfiguration, PlatformName
 
@@ -28,10 +30,27 @@ def build(self, options: Options, tmp_path: Path) -> None: ...
     "windows": windows,
     "macos": macos,
     "pyodide": pyodide,
+    "android": android,
     "ios": ios,
 }
 
 
+def native_platform() -> PlatformName:
+    if sys.platform.startswith("linux"):
+        return "linux"
+    elif sys.platform == "darwin":
+        return "macos"
+    elif sys.platform == "win32":
+        return "windows"
+    else:
+        msg = (
+            'Unable to detect platform from "sys.platform". cibuildwheel doesn\'t '
+            "support building wheels for this platform. You might be able to build for a different "
+            "platform using the --platform argument. Check --help output for more information."
+        )
+        raise errors.ConfigurationError(msg)
+
+
 def get_build_identifiers(
     platform_module: PlatformModule,
     build_selector: BuildSelector,
diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py
new file mode 100644
index 000000000..52d4898cb
--- /dev/null
+++ b/cibuildwheel/platforms/android.py
@@ -0,0 +1,644 @@
+import csv
+import hashlib
+import os
+import platform
+import re
+import shlex
+import shutil
+import subprocess
+from collections.abc import Iterable, Iterator
+from dataclasses import dataclass
+from os.path import relpath
+from pathlib import Path
+from pprint import pprint
+from runpy import run_path
+from textwrap import dedent
+from typing import Any
+
+from build import ProjectBuilder
+from build.env import IsolatedEnv
+from elftools.common.exceptions import ELFError
+from elftools.elf.elffile import ELFFile
+from filelock import FileLock
+
+from .. import errors, platforms
+from ..architecture import Architecture, arch_synonym
+from ..frontend import get_build_frontend_extra_flags, parse_config_settings
+from ..logger import log
+from ..options import BuildOptions, Options
+from ..selector import BuildSelector
+from ..util import resources
+from ..util.cmd import call, shell
+from ..util.file import CIBW_CACHE_PATH, copy_test_sources, download, move_file
+from ..util.helpers import prepare_command
+from ..util.packaging import find_compatible_wheel
+from ..util.python_build_standalone import create_python_build_standalone_environment
+from ..venv import constraint_flags, virtualenv
+
+
+def android_triplet(identifier: str) -> str:
+    return {
+        "arm64_v8a": "aarch64-linux-android",
+        "x86_64": "x86_64-linux-android",
+    }[parse_identifier(identifier)[1]]
+
+
+def parse_identifier(identifier: str) -> tuple[str, str]:
+    match = re.fullmatch(r"cp(\d)(\d+)-android_(.+)", identifier)
+    if not match:
+        msg = f"invalid Android identifier: '{identifier}'"
+        raise ValueError(msg)
+    major, minor, arch = match.groups()
+    return (f"{major}.{minor}", arch)
+
+
+@dataclass(frozen=True)
+class PythonConfiguration:
+    version: str
+    identifier: str
+    url: str
+
+    @property
+    def arch(self) -> str:
+        return parse_identifier(self.identifier)[1]
+
+
+def all_python_configurations() -> list[PythonConfiguration]:
+    return [PythonConfiguration(**item) for item in resources.read_python_configs("android")]
+
+
+def get_python_configurations(
+    build_selector: BuildSelector, architectures: set[Architecture]
+) -> list[PythonConfiguration]:
+    return [
+        c
+        for c in all_python_configurations()
+        if c.arch in architectures and build_selector(c.identifier)
+    ]
+
+
+def shell_prepared(command: str, *, build_options: BuildOptions, env: dict[str, str]) -> None:
+    shell(
+        prepare_command(command, project=".", package=build_options.package_dir),
+        env=env,
+    )
+
+
+def before_all(options: Options, python_configurations: list[PythonConfiguration]) -> None:
+    before_all_options = options.build_options(python_configurations[0].identifier)
+    if before_all_options.before_all:
+        log.step("Running before_all...")
+        shell_prepared(
+            before_all_options.before_all,
+            build_options=before_all_options,
+            env=before_all_options.environment.as_dictionary(os.environ),
+        )
+
+
+@dataclass(frozen=True)
+class BuildState:
+    config: PythonConfiguration
+    options: BuildOptions
+    build_path: Path
+    python_dir: Path
+    build_env: dict[str, str]
+    android_env: dict[str, str]
+
+
+def build(options: Options, tmp_path: Path) -> None:
+    if "ANDROID_HOME" not in os.environ:
+        msg = (
+            "ANDROID_HOME environment variable is not set. For instructions, see "
+            "https://cibuildwheel.pypa.io/en/stable/platforms/#android"
+        )
+        raise errors.FatalError(msg)
+
+    configs = get_python_configurations(
+        options.globals.build_selector, options.globals.architectures
+    )
+    if not configs:
+        return
+
+    try:
+        before_all(options, configs)
+
+        built_wheels: list[Path] = []
+        for config in configs:
+            log.build_start(config.identifier)
+            build_options = options.build_options(config.identifier)
+            build_path = tmp_path / config.identifier
+            build_path.mkdir()
+            python_dir = setup_target_python(config, build_path)
+            build_env, android_env = setup_env(config, build_options, build_path, python_dir)
+            state = BuildState(
+                config, build_options, build_path, python_dir, build_env, android_env
+            )
+
+            compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
+            if compatible_wheel:
+                print(
+                    f"\nFound previously built wheel {compatible_wheel.name} that is "
+                    f"compatible with {config.identifier}. Skipping build step..."
+                )
+                repaired_wheel = compatible_wheel
+            else:
+                before_build(state)
+                built_wheel = build_wheel(state)
+                repaired_wheel = repair_wheel(state, built_wheel)
+
+            test_wheel(state, repaired_wheel)
+
+            output_wheel: Path | None = None
+            if compatible_wheel is None:
+                output_wheel = move_file(
+                    repaired_wheel, build_options.output_dir / repaired_wheel.name
+                )
+                built_wheels.append(output_wheel)
+
+            shutil.rmtree(build_path)
+            log.build_end(output_wheel)
+
+    except subprocess.CalledProcessError as error:
+        msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
+        raise errors.FatalError(msg) from error
+
+
+def setup_target_python(config: PythonConfiguration, build_path: Path) -> Path:
+    log.step("Installing target Python...")
+    python_tgz = CIBW_CACHE_PATH / config.url.rpartition("/")[-1]
+    with FileLock(f"{python_tgz}.lock"):
+        if not python_tgz.exists():
+            download(config.url, python_tgz)
+
+    python_dir = build_path / "python"
+    python_dir.mkdir()
+    shutil.unpack_archive(python_tgz, python_dir)
+    return python_dir
+
+
+def setup_env(
+    config: PythonConfiguration, build_options: BuildOptions, build_path: Path, python_dir: Path
+) -> tuple[dict[str, str], dict[str, str]]:
+    """
+    Returns two environment dicts, both pointing at the same virtual environment:
+
+    * build_env, which uses the environment normally.
+    * android_env, which uses the environment while simulating running on Android.
+    """
+    log.step("Setting up build environment...")
+
+    # Create virtual environment
+    python_exe = create_python_build_standalone_environment(
+        config.version, build_path, CIBW_CACHE_PATH
+    )
+    venv_dir = build_path / "venv"
+    dependency_constraint = build_options.dependency_constraints.get_for_python_version(
+        version=config.version, tmp_dir=build_path
+    )
+    build_env = virtualenv(
+        config.version, python_exe, venv_dir, dependency_constraint, use_uv=False
+    )
+    create_cmake_toolchain(config, build_path, python_dir, build_env)
+
+    # Apply custom environment variables, and check environment is still valid
+    build_env = build_options.environment.as_dictionary(build_env)
+    build_env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
+    for command in ["python", "pip"]:
+        which = call("which", command, env=build_env, capture_stdout=True).strip()
+        if which != f"{venv_dir}/bin/{command}":
+            msg = (
+                f"{command} available on PATH doesn't match our installed instance. If you "
+                f"have modified PATH, ensure that you don't overwrite cibuildwheel's entry "
+                f"or insert {command} above it."
+            )
+            raise errors.FatalError(msg)
+        call(command, "--version", env=build_env)
+
+    # Construct an altered environment which simulates running on Android.
+    android_env = setup_android_env(config, python_dir, venv_dir, build_env)
+
+    # Install build tools
+    build_frontend = build_options.build_frontend
+    if build_frontend.name != "build":
+        msg = "Android requires the build frontend to be 'build'"
+        raise errors.FatalError(msg)
+    call("pip", "install", "build", *constraint_flags(dependency_constraint), env=build_env)
+
+    # Build-time requirements must be queried within android_env, because
+    # `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be
+    # affected by the target platform. However, the requirements must be installed
+    # within build_env, because they're going to run on the build machine.
+    #
+    # The `build` CLI doesn't support this combination, so we use its API to query the
+    # requirements, and then install them ourselves with pip. We'll later run `build` in
+    # the same environment, passing the `--no-isolation` option.
+    class AndroidEnv(IsolatedEnv):
+        @property
+        def python_executable(self) -> str:
+            return f"{venv_dir}/bin/python"
+
+        def make_extra_environ(self) -> dict[str, str]:
+            return android_env
+
+    pb = ProjectBuilder.from_isolated_env(AndroidEnv(), build_options.package_dir)
+    if pb.build_system_requires:
+        call("pip", "install", *pb.build_system_requires, env=build_env)
+
+    requires_for_build = pb.get_requires_for_build(
+        "wheel", parse_config_settings(build_options.config_settings)
+    )
+    if requires_for_build:
+        call("pip", "install", *requires_for_build, env=build_env)
+
+    return build_env, android_env
+
+
+def create_cmake_toolchain(
+    config: PythonConfiguration, build_path: Path, python_dir: Path, build_env: dict[str, str]
+) -> None:
+    toolchain_path = build_path / "toolchain.cmake"
+    build_env["CMAKE_TOOLCHAIN_FILE"] = str(toolchain_path)
+    with open(toolchain_path, "w", encoding="UTF-8") as toolchain_file:
+        print(
+            dedent(
+                f"""\
+                # To support as many build systems as possible, we use environment
+                # variables as the single source of truth for compiler flags and paths,
+                # so they don't need to be specified here.
+
+                set(CMAKE_SYSTEM_NAME Android)
+                set(CMAKE_SYSTEM_PROCESSOR {android_triplet(config.identifier).split("-")[0]})
+
+                # Inhibit all of CMake's own NDK handling code.
+                set(CMAKE_SYSTEM_VERSION 1)
+
+                # Tell CMake where to look for headers and libraries.
+                list(INSERT CMAKE_FIND_ROOT_PATH 0 {python_dir}/prefix)
+                set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+                set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+                set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+                set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+                """
+            ),
+            file=toolchain_file,
+        )
+
+
+def localize_sysconfigdata(
+    python_dir: Path, build_env: dict[str, str], sysconfigdata_path: Path
+) -> dict[str, Any]:
+    sysconfigdata: dict[str, Any] = run_path(str(sysconfigdata_path))["build_time_vars"]
+    with sysconfigdata_path.open("w", encoding="UTF-8") as f:
+        f.write("# Generated by cibuildwheel\n")
+        f.write("build_time_vars = ")
+        sysconfigdata = localized_vars(build_env, sysconfigdata, python_dir / "prefix")
+        pprint(sysconfigdata, stream=f, compact=True)
+        return sysconfigdata
+
+
+def localized_vars(
+    build_env: dict[str, str], orig_vars: dict[str, Any], prefix: Path
+) -> dict[str, Any]:
+    orig_prefix = orig_vars["prefix"]
+    localized_vars_ = {}
+    for key, value in orig_vars.items():
+        # The host's sysconfigdata will include references to build-time paths.
+        # Update these to refer to the current prefix.
+        final = value
+        if isinstance(final, str):
+            final = final.replace(orig_prefix, str(prefix))
+
+        if key == "ANDROID_API_LEVEL":
+            if api_level := build_env.get(key):
+                final = int(api_level)
+
+        # Build systems vary in whether FLAGS variables are read from sysconfig, and if so,
+        # whether they're replaced by environment variables or combined with them. Even
+        # setuptools has changed its behavior here
+        # (https://github.com/pypa/setuptools/issues/4836).
+        #
+        # Ensure consistency by clearing the sysconfig variables and letting the environment
+        # variables take effect alone. This will also work for any non-Python build systems
+        # which the build script may call.
+        elif key in ["CFLAGS", "CXXFLAGS", "LDFLAGS"]:
+            final = ""
+
+        # These variables contain an embedded copy of LDFLAGS.
+        elif key in ["LDSHARED", "LDCXXSHARED"]:
+            final = final.removesuffix(" " + orig_vars["LDFLAGS"])
+
+        localized_vars_[key] = final
+
+    return localized_vars_
+
+
+def setup_android_env(
+    config: PythonConfiguration, python_dir: Path, venv_dir: Path, build_env: dict[str, str]
+) -> dict[str, str]:
+    site_packages = next(venv_dir.glob("lib/python*/site-packages"))
+    for suffix in ["pth", "py"]:
+        shutil.copy(resources.PATH / f"_cross_venv.{suffix}", site_packages)
+
+    sysconfigdata_path = Path(
+        shutil.copy(
+            next(python_dir.glob("prefix/lib/python*/_sysconfigdata_*.py")),
+            site_packages,
+        )
+    )
+    sysconfigdata = localize_sysconfigdata(python_dir, build_env, sysconfigdata_path)
+
+    # Activate the code in _cross_venv.py.
+    android_env = build_env.copy()
+    android_env["CIBW_HOST_TRIPLET"] = android_triplet(config.identifier)
+
+    # Get the environment variables needed to build for Android (CC, CFLAGS, etc). These are
+    # generated by https://github.com/python/cpython/blob/main/Android/android-env.sh.
+    env_output = call(python_dir / "android.py", "env", env=build_env, capture_stdout=True)
+
+    # shlex.split should produce a sequence alternating between:
+    #   * the word "export"
+    #   * a key=value string, without quotes
+    for i, token in enumerate(shlex.split(env_output)):
+        if i % 2 == 0:
+            assert token == "export", token
+        else:
+            key, sep, value = token.partition("=")
+            assert sep == "=", token
+            android_env[key] = value
+
+    # localized_vars cleared the CFLAGS and CXXFLAGS in the sysconfigdata, but most
+    # packages take their optimization flags from these variables. Pass these flags via
+    # environment variables instead.
+    #
+    # We don't enable debug information, because it significantly increases binary size,
+    # and most Android app developers don't have the NDK installed, so they would have no
+    # way to strip it.
+    opt = " ".join(word for word in sysconfigdata["OPT"].split() if not word.startswith("-g"))
+    for key in ["CFLAGS", "CXXFLAGS"]:
+        android_env[key] += " " + opt
+
+    # Format the environment so it can be pasted into a shell when debugging.
+    for key, value in sorted(android_env.items()):
+        if os.environ.get(key) != value:
+            print(f"export {key}={shlex.quote(value)}")
+
+    return android_env
+
+
+def before_build(state: BuildState) -> None:
+    if state.options.before_build:
+        log.step("Running before_build...")
+        shell_prepared(
+            state.options.before_build,
+            build_options=state.options,
+            env=state.build_env,
+        )
+
+
+def build_wheel(state: BuildState) -> Path:
+    log.step("Building wheel...")
+    built_wheel_dir = state.build_path / "built_wheel"
+    call(
+        "python",
+        "-m",
+        "build",
+        state.options.package_dir,
+        "--wheel",
+        "--no-isolation",
+        "--skip-dependency-check",
+        f"--outdir={built_wheel_dir}",
+        *get_build_frontend_extra_flags(
+            state.options.build_frontend,
+            state.options.build_verbosity,
+            state.options.config_settings,
+        ),
+        env=state.android_env,
+    )
+
+    built_wheels = list(built_wheel_dir.glob("*.whl"))
+    if len(built_wheels) != 1:
+        msg = f"{built_wheel_dir} contains {len(built_wheels)} wheels; expected 1"
+        raise errors.FatalError(msg)
+    built_wheel = built_wheels[0]
+
+    if built_wheel.name.endswith("none-any.whl"):
+        raise errors.NonPlatformWheelError()
+    return built_wheel
+
+
+def repair_wheel(state: BuildState, built_wheel: Path) -> Path:
+    log.step("Repairing wheel...")
+    repaired_wheel_dir = state.build_path / "repaired_wheel"
+    repaired_wheel_dir.mkdir()
+
+    if state.options.repair_command:
+        shell(
+            prepare_command(
+                state.options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
+            ),
+            env=state.build_env,
+        )
+    else:
+        repair_default(state.android_env, built_wheel, repaired_wheel_dir)
+
+    repaired_wheels = list(repaired_wheel_dir.glob("*.whl"))
+    if len(repaired_wheels) == 0:
+        raise errors.RepairStepProducedNoWheelError()
+    if len(repaired_wheels) != 1:
+        msg = f"{repaired_wheel_dir} contains {len(repaired_wheels)} wheels; expected 1"
+        raise errors.FatalError(msg)
+    repaired_wheel = repaired_wheels[0]
+
+    if repaired_wheel.name.endswith("none-any.whl"):
+        raise errors.NonPlatformWheelError()
+    return repaired_wheel
+
+
+def repair_default(
+    android_env: dict[str, str], built_wheel: Path, repaired_wheel_dir: Path
+) -> None:
+    """
+    Adds libc++ to the wheel if anything links against it. In the future this should be
+    moved to auditwheel and generalized to support more libraries.
+    """
+    if (match := re.search(r"^(.+?)-", built_wheel.name)) is None:
+        msg = f"Failed to parse wheel filename: {built_wheel.name}"
+        raise errors.FatalError(msg)
+    wheel_name = match[1]
+
+    unpacked_dir = repaired_wheel_dir / "unpacked"
+    unpacked_dir.mkdir()
+    shutil.unpack_archive(built_wheel, unpacked_dir, format="zip")
+
+    # Some build systems are inconsistent about name normalization, so don't assume the
+    # dist-info name is identical to the wheel name.
+    record_paths = list(unpacked_dir.glob("*.dist-info/RECORD"))
+    if len(record_paths) != 1:
+        msg = f"{built_wheel.name} contains {len(record_paths)} dist-info/RECORD files; expected 1"
+        raise errors.FatalError(msg)
+
+    old_soname = "libc++_shared.so"
+    paths_to_patch = []
+    for path, elffile in elf_file_filter(
+        unpacked_dir / filename
+        for filename, *_ in csv.reader(record_paths[0].read_text().splitlines())
+    ):
+        if (dynamic := elffile.get_section_by_name(".dynamic")) and any(  # type: ignore[no-untyped-call]
+            tag.entry.d_tag == "DT_NEEDED" and tag.needed == old_soname
+            for tag in dynamic.iter_tags()
+        ):
+            paths_to_patch.append(path)
+
+    if not paths_to_patch:
+        shutil.copyfile(built_wheel, repaired_wheel_dir / built_wheel.name)
+    else:
+        # Android doesn't support DT_RPATH, but supports DT_RUNPATH since API level 24
+        # (https://github.com/aosp-mirror/platform_bionic/blob/master/android-changes-for-ndk-developers.md).
+        if int(sysconfig_print('get_config_vars()["ANDROID_API_LEVEL"]', android_env)) < 24:
+            msg = f"Adding {old_soname} requires ANDROID_API_LEVEL to be at least 24"
+            raise errors.FatalError(msg)
+
+        toolchain = Path(android_env["CC"]).parent.parent
+        src_path = toolchain / f"sysroot/usr/lib/{android_env['CIBW_HOST_TRIPLET']}/{old_soname}"
+
+        # Use the same library location as auditwheel would.
+        libs_dir = unpacked_dir / (wheel_name + ".libs")
+        libs_dir.mkdir()
+        new_soname = soname_with_hash(src_path)
+        dst_path = libs_dir / new_soname
+        shutil.copyfile(src_path, dst_path)
+        call("patchelf", "--set-soname", new_soname, dst_path)
+
+        for path in paths_to_patch:
+            call("patchelf", "--replace-needed", old_soname, new_soname, path)
+            call(
+                "patchelf",
+                "--set-rpath",
+                f"${{ORIGIN}}/{relpath(libs_dir, path.parent)}",
+                path,
+            )
+        call("wheel", "pack", unpacked_dir, "-d", repaired_wheel_dir)
+
+
+def elf_file_filter(paths: Iterable[Path]) -> Iterator[tuple[Path, ELFFile]]:
+    """Filter through an iterator of filenames and load up only ELF files"""
+    for path in paths:
+        if not path.name.endswith(".py"):
+            try:
+                with open(path, "rb") as f:
+                    candidate = ELFFile(f)  # type: ignore[no-untyped-call]
+                    yield path, candidate
+            except ELFError:
+                pass  # Not an ELF file
+
+
+def soname_with_hash(src_path: Path) -> str:
+    """Return the same library filename as auditwheel would"""
+    shorthash = hashlib.sha256(src_path.read_bytes()).hexdigest()[:8]
+    src_name = src_path.name
+    base, ext = src_name.split(".", 1)
+    if not base.endswith(f"-{shorthash}"):
+        return f"{base}-{shorthash}.{ext}"
+    else:
+        return src_name
+
+
+def test_wheel(state: BuildState, wheel: Path) -> None:
+    test_command = state.options.test_command
+    if not (test_command and state.options.test_selector(state.config.identifier)):
+        return
+
+    log.step("Testing wheel...")
+    native_arch = arch_synonym(platform.machine(), platforms.native_platform(), "android")
+    if state.config.arch != native_arch:
+        log.warning(
+            f"Skipping tests for {state.config.arch}, as the build machine only "
+            f"supports {native_arch}"
+        )
+        return
+
+    if state.options.before_test:
+        shell_prepared(
+            state.options.before_test,
+            build_options=state.options,
+            env=state.build_env,
+        )
+
+    # Install the wheel and test-requires.
+    site_packages_dir = state.build_path / "site-packages"
+    site_packages_dir.mkdir()
+    call(
+        "pip",
+        "install",
+        "--only-binary=:all:",
+        "--platform",
+        sysconfig_print("get_platform()", state.android_env).replace("-", "_"),
+        "--target",
+        site_packages_dir,
+        f"{wheel}{state.options.test_extras}",
+        *state.options.test_requires,
+        env=state.build_env,
+    )
+
+    # Copy test-sources.
+    cwd_dir = state.build_path / "cwd"
+    cwd_dir.mkdir()
+    if state.options.test_sources:
+        copy_test_sources(state.options.test_sources, Path.cwd(), cwd_dir)
+    else:
+        (cwd_dir / "test_fail.py").write_text(
+            resources.TEST_FAIL_CWD_FILE.read_text(),
+        )
+
+    # Android doesn't support placeholders in the test command.
+    if any(("{" + placeholder + "}") in test_command for placeholder in ["project", "package"]):
+        msg = (
+            f"Test command {test_command!r} with a "
+            "'{project}' or '{package}' placeholder is not supported on Android, "
+            "because the source directory is not visible on the emulator."
+        )
+        raise errors.FatalError(msg)
+
+    # Parse test-command.
+    test_args = shlex.split(test_command)
+    if test_args[:2] in [["python", "-c"], ["python", "-m"]]:
+        test_args[:3] = [test_args[1], test_args[2], "--"]
+    elif test_args[0] in ["pytest"]:
+        # We transform some commands into the `python -m` form, but this is deprecated.
+        msg = (
+            f"Test command {test_command!r} is not supported on Android. "
+            "cibuildwheel will try to execute it as if it started with 'python -m'. "
+            "If this works, all you need to do is add that to your test command."
+        )
+        log.warning(msg)
+        test_args[:1] = ["-m", test_args[0], "--"]
+    else:
+        msg = (
+            f"Test command {test_command!r} is not supported on Android. "
+            f"Supported commands are 'python -m' and 'python -c'."
+        )
+        raise errors.FatalError(msg)
+
+    # Run the test app.
+    call(
+        state.python_dir / "android.py",
+        "test",
+        "--managed",
+        "maxVersion",
+        "--site-packages",
+        site_packages_dir,
+        "--cwd",
+        cwd_dir,
+        *test_args,
+        env=state.build_env,
+    )
+
+
+def sysconfig_print(method_call: str, env: dict[str, str]) -> str:
+    return call(
+        "python",
+        "-c",
+        f'import sysconfig; print(sysconfig.{method_call}, end="")',
+        env=env,
+        capture_stdout=True,
+    )
diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py
index f4cf6b60d..eba04c35d 100644
--- a/cibuildwheel/platforms/ios.py
+++ b/cibuildwheel/platforms/ios.py
@@ -17,7 +17,6 @@
 from ..architecture import Architecture
 from ..environment import ParsedEnvironment
 from ..frontend import (
-    BuildFrontendConfig,
     BuildFrontendName,
     get_build_frontend_extra_flags,
 )
@@ -438,7 +437,7 @@ def build(options: Options, tmp_path: Path) -> None:
 
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
-            build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
+            build_frontend = build_options.build_frontend
             # uv doesn't support iOS
             if build_frontend.name == "build[uv]":
                 msg = "uv doesn't support iOS"
diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py
index 8d25d73d0..8411a935a 100644
--- a/cibuildwheel/platforms/linux.py
+++ b/cibuildwheel/platforms/linux.py
@@ -10,7 +10,7 @@
 
 from .. import errors
 from ..architecture import Architecture
-from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags
+from ..frontend import get_build_frontend_extra_flags
 from ..logger import log
 from ..oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform
 from ..options import BuildOptions, Options
@@ -204,7 +204,7 @@ def build_in_container(
         log.build_start(config.identifier)
         local_identifier_tmp_dir = local_tmp_dir / config.identifier
         build_options = options.build_options(config.identifier)
-        build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
+        build_frontend = build_options.build_frontend
         use_uv = build_frontend.name == "build[uv]"
         pip = ["uv", "pip"] if use_uv else ["pip"]
 
diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py
index a273f062c..05d5d1623 100644
--- a/cibuildwheel/platforms/macos.py
+++ b/cibuildwheel/platforms/macos.py
@@ -19,7 +19,7 @@
 from ..architecture import Architecture
 from ..ci import detect_ci_provider
 from ..environment import ParsedEnvironment
-from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags
+from ..frontend import BuildFrontendName, get_build_frontend_extra_flags
 from ..logger import log
 from ..options import Options
 from ..selector import BuildSelector
@@ -408,7 +408,7 @@ def build(options: Options, tmp_path: Path) -> None:
 
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
-            build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
+            build_frontend = build_options.build_frontend
             use_uv = build_frontend.name == "build[uv]"
             uv_path = find_uv()
             if use_uv and uv_path is None:
diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py
index c46248ee7..1871fca40 100644
--- a/cibuildwheel/platforms/pyodide.py
+++ b/cibuildwheel/platforms/pyodide.py
@@ -17,7 +17,7 @@
 from .. import errors
 from ..architecture import Architecture
 from ..environment import ParsedEnvironment
-from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags
+from ..frontend import get_build_frontend_extra_flags
 from ..logger import log
 from ..options import Options
 from ..selector import BuildSelector
@@ -353,7 +353,7 @@ def build(options: Options, tmp_path: Path) -> None:
 
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
-            build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
+            build_frontend = build_options.build_frontend
 
             if build_frontend.name == "pip":
                 msg = "The pyodide platform doesn't support pip frontend"
diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py
index 5a55b3a08..f9c85be89 100644
--- a/cibuildwheel/platforms/windows.py
+++ b/cibuildwheel/platforms/windows.py
@@ -15,7 +15,7 @@
 from .. import errors
 from ..architecture import Architecture
 from ..environment import ParsedEnvironment
-from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags
+from ..frontend import BuildFrontendName, get_build_frontend_extra_flags
 from ..logger import log
 from ..options import Options
 from ..selector import BuildSelector
@@ -395,7 +395,7 @@ def build(options: Options, tmp_path: Path) -> None:
 
         for config in python_configurations:
             build_options = options.build_options(config.identifier)
-            build_frontend = build_options.build_frontend or BuildFrontendConfig("build")
+            build_frontend = build_options.build_frontend
 
             use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
             log.build_start(config.identifier)
diff --git a/cibuildwheel/resources/_cross_venv.pth b/cibuildwheel/resources/_cross_venv.pth
new file mode 100644
index 000000000..c8a04bc64
--- /dev/null
+++ b/cibuildwheel/resources/_cross_venv.pth
@@ -0,0 +1 @@
+import _cross_venv; _cross_venv.initialize()
diff --git a/cibuildwheel/resources/_cross_venv.py b/cibuildwheel/resources/_cross_venv.py
new file mode 100644
index 000000000..84237a2eb
--- /dev/null
+++ b/cibuildwheel/resources/_cross_venv.py
@@ -0,0 +1,83 @@
+# This module is copied into the site-packages directory of an Android build environment, and
+# activated via a .pth file when we want the environment to simulate Android.
+
+import os
+import platform
+import re
+import sys
+import sysconfig
+from pathlib import Path
+from typing import Any
+
+
+def initialize() -> None:
+    if not (host_triplet := os.environ.get("CIBW_HOST_TRIPLET")):
+        return
+
+    # os ######################################################################
+    def cross_os_uname() -> os.uname_result:
+        return os.uname_result(
+            (
+                "Linux",
+                "localhost",
+                # The Linux kernel version and release are unlikely to be significant, but return
+                # realistic values anyway (from an API level 24 emulator).
+                "3.18.91+",
+                "#1 SMP PREEMPT Tue Jan 9 20:35:43 UTC 2018",
+                host_triplet.split("-")[0],
+            )
+        )
+
+    os.name = "posix"
+    os.uname = cross_os_uname
+
+    # platform ################################################################
+    #
+    # We can't determine the user-visible Android version number from the API level, so return a
+    # string which will work fine for display, but will fail to parse as a version number.
+    def cross_android_ver(*args: Any, **kwargs: Any) -> platform.AndroidVer:
+        return platform.AndroidVer(
+            release=f"API level {cross_getandroidapilevel()}",
+            api_level=cross_getandroidapilevel(),
+            manufacturer="Google",
+            model="sdk_gphone64",
+            device="emu64",
+            is_emulator=True,
+        )
+
+    # platform.uname, platform.system etc. are all implemented in terms of platform.android_ver.
+    platform.android_ver = cross_android_ver
+
+    # sys #####################################################################
+    def cross_getandroidapilevel() -> int:
+        api_level = sysconfig.get_config_var("ANDROID_API_LEVEL")
+        assert isinstance(api_level, int)
+        return api_level
+
+    # Some packages may recognize sys.cross_compiling from the crossenv tool.
+    sys.cross_compiling = True  # type: ignore[attr-defined]
+    sys.getandroidapilevel = cross_getandroidapilevel  # type: ignore[attr-defined]
+    sys.implementation._multiarch = host_triplet  # type: ignore[attr-defined]
+    sys.platform = "android"
+
+    # Determine the abiflags from the sysconfigdata filename.
+    sysconfigdata_path = next(Path(__file__).parent.glob("_sysconfigdata_*.py"))
+    abiflags_match = re.match(r"_sysconfigdata_(.*?)_", sysconfigdata_path.name)
+    assert abiflags_match is not None
+    sys.abiflags = abiflags_match[1]
+
+    # sysconfig ###############################################################
+    #
+    # We don't change the actual sys.base_prefix and base_exec_prefix, because that
+    # could have unpredictable effects. Instead, we change the internal variables
+    # used to generate sysconfig.get_path("include").
+    exec_prefix = sysconfig.get_config_var("exec_prefix")
+    sysconfig._BASE_PREFIX = sysconfig._BASE_EXEC_PREFIX = exec_prefix  # type: ignore[attr-defined]
+
+    # Reload the sysconfigdata file, generating its name from sys.abiflags,
+    # sys.platform, and sys.implementation._multiarch.
+    sysconfig._init_config_vars()  # type: ignore[attr-defined]
+
+    # sysconfig.get_platform, which determines the wheel tag, is implemented in terms of
+    # sys.platform, sysconfig.get_config_var("ANDROID_API_LEVEL") (see localized_vars in
+    # android.py), and os.uname.
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 50904fdc6..6f5b7c791 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -224,6 +224,12 @@ python_configurations = [
   { identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.28.0", node_version = "v22" },
 ]
 
+[android]
+python_configurations = [
+  { identifier = "cp313-android_arm64_v8a", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.5+20250722.214220/python-3.13.5+20250722.214220-aarch64-linux-android.tar.gz" },
+  { identifier = "cp313-android_x86_64", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.5+20250722.214220/python-3.13.5+20250722.214220-x86_64-linux-android.tar.gz" },
+]
+
 [ios]
 python_configurations = [
   { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 7d6aaf10d..1b00884d3 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -108,7 +108,7 @@
     },
     "build-frontend": {
       "default": "default",
-      "description": "Set the tool to use to build, either \"pip\" (default for now), \"build\", or \"build[uv]\"",
+      "description": "Set the tool to use to build, either \"build\" (default), \"build[uv]\", or \"pip\"",
       "oneOf": [
         {
           "enum": [
@@ -1072,6 +1072,57 @@
         }
       }
     },
+    "android": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "archs": {
+          "$ref": "#/properties/archs"
+        },
+        "before-all": {
+          "$ref": "#/properties/before-all"
+        },
+        "before-build": {
+          "$ref": "#/properties/before-build"
+        },
+        "before-test": {
+          "$ref": "#/properties/before-test"
+        },
+        "build-frontend": {
+          "$ref": "#/properties/build-frontend"
+        },
+        "build-verbosity": {
+          "$ref": "#/properties/build-verbosity"
+        },
+        "config-settings": {
+          "$ref": "#/properties/config-settings"
+        },
+        "dependency-versions": {
+          "$ref": "#/properties/dependency-versions"
+        },
+        "environment": {
+          "$ref": "#/properties/environment"
+        },
+        "repair-wheel-command": {
+          "$ref": "#/properties/repair-wheel-command"
+        },
+        "test-command": {
+          "$ref": "#/properties/test-command"
+        },
+        "test-extras": {
+          "$ref": "#/properties/test-extras"
+        },
+        "test-sources": {
+          "$ref": "#/properties/test-sources"
+        },
+        "test-groups": {
+          "$ref": "#/properties/test-groups"
+        },
+        "test-requires": {
+          "$ref": "#/properties/test-requires"
+        }
+      }
+    },
     "ios": {
       "type": "object",
       "additionalProperties": false,
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index 9901ec895..d5c176f27 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -58,6 +58,8 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest
 
 [tool.cibuildwheel.windows]
 
+[tool.cibuildwheel.android]
+
 [tool.cibuildwheel.ios]
 
 [tool.cibuildwheel.pyodide]
diff --git a/cibuildwheel/resources/testing_temp_dir_file.py b/cibuildwheel/resources/testing_temp_dir_file.py
index 094f2f6fb..cc3e15a2b 100644
--- a/cibuildwheel/resources/testing_temp_dir_file.py
+++ b/cibuildwheel/resources/testing_temp_dir_file.py
@@ -8,11 +8,11 @@
 
 class TestStringMethods(unittest.TestCase):
     def test_fail(self) -> NoReturn:
-        if sys.platform == "ios":
+        if sys.platform in ["android", "ios"]:
             msg = (
                 "You tried to run tests from the testbed app's working "
                 "directory, without specifying `test-sources`. "
-                "On iOS, you must copy your test files to the testbed app by "
+                "On this platform, you must copy your test files to the testbed app by "
                 "setting the `test-sources` option in your cibuildwheel "
                 "configuration."
             )
diff --git a/cibuildwheel/typing.py b/cibuildwheel/typing.py
index 420ef05f6..53b0887da 100644
--- a/cibuildwheel/typing.py
+++ b/cibuildwheel/typing.py
@@ -12,7 +12,7 @@
 PathOrStr = str | os.PathLike[str]
 
 
-PlatformName = Literal["linux", "macos", "windows", "pyodide", "ios"]
+PlatformName = Literal["linux", "macos", "windows", "pyodide", "android", "ios"]
 PLATFORMS: Final[frozenset[PlatformName]] = frozenset(typing.get_args(PlatformName))
 
 
diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py
index 295bc091c..578f82e44 100644
--- a/cibuildwheel/util/packaging.py
+++ b/cibuildwheel/util/packaging.py
@@ -158,9 +158,9 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
                 # If a minor version number is given, it has to be lower than the current one.
                 continue
 
-            if platform.startswith(("manylinux", "musllinux", "macosx", "ios")):
-                # Linux, macOS, and iOS require the beginning and ending match
-                # (macos/manylinux/iOS version number doesn't need to match)
+            if platform.startswith(("manylinux", "musllinux", "macosx", "android", "ios")):
+                # On these platforms the wheel tag includes a platform version number, which we
+                # should ignore.
                 os_, arch = platform.split("_", 1)
                 if not tag.platform.startswith(os_):
                     continue
diff --git a/docs/changelog.md b/docs/changelog.md
index a6d61d49e..56eaf599e 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -22,10 +22,10 @@ See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) f
 - ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b2. (#2390)
 
     _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
-- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), and changes the working directory for tests. (#2062, #2284, #2437)
 
-    - If this option is set, cibuildwheel will copy the files and folders specified in `test-sources` into the temporary directory we run from. This is required for iOS builds, but also useful for other platforms, as it allows you to avoid placeholders.
-    - If this option is not set, behaviour matches v2.x - cibuildwheel will run the tests from a temporary directory, and you can use the `{project}` placeholder in the `test-command` to refer to the project directory. (#2420)
+- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), which copies files and folders into the temporary working directory we run tests from. (#2062, #2284, #2420, #2437)
+
+    This is particularly important for iOS builds, which do not support placeholders in the `test-command`, but can also be useful for other platforms.
 
 - ✨ Adds [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) inline syntax (#2122)
 - ✨ Improves support for Pyodide builds and adds the experimental [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
diff --git a/docs/ci-services.md b/docs/ci-services.md
index 6564052f8..ecf4f567e 100644
--- a/docs/ci-services.md
+++ b/docs/ci-services.md
@@ -53,7 +53,7 @@ Commit this file, and push to GitHub - either to your default branch, or to a PR
 
 For more info on this file, check out the [docs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions).
 
-[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example to include iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI.
+[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) extends this minimal example to include Android, iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI.
 
 
 ### Azure Pipelines [linux/mac/windows] {: #azure-pipelines}
diff --git a/docs/options.md b/docs/options.md
index b4fdf6361..735870496 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -8,7 +8,7 @@
 
 > Override the auto-detected target platform
 
-Options: `auto` `linux` `macos` `windows` `ios` `pyodide`
+Options: `auto` `linux` `macos` `windows` `android` `ios` `pyodide`
 
 Default: `auto`
 
@@ -16,6 +16,7 @@ Default: `auto`
 
 - For `linux`, you need [Docker or Podman](#container-engine) running, on Linux, macOS, or Windows.
 - For `macos` and `windows`, you need to be running on the respective system, with a working compiler toolchain installed - Xcode Command Line tools for macOS, and MSVC for Windows.
+- For `android` you need to be running on Linux or macOS, with an Android SDK installed. See [here](platforms.md#android) for more details.
 - For `ios` you need to be running on macOS, with Xcode and the iOS simulator installed.
 - For `pyodide`, you need a Linux or macOS machine.
 
@@ -51,19 +52,19 @@ When both options are specified, both conditions are applied and only builds wit
 When setting the options, you can use shell-style globbing syntax, as per [fnmatch](https://docs.python.org/3/library/fnmatch.html) with the addition of curly bracket syntax `{option1,option2}`, provided by [bracex](https://pypi.org/project/bracex/). All the build identifiers supported by cibuildwheel are shown below:
 
 <div class="build-id-table-marker"></div>
-|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                                                                                   | iOS                                                                                               | pyodide (WASM)       |
-|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|----------------------|
-| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-manylinux_riscv64<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l<br/>cp38-musllinux_riscv64           |                                                                                                   |                      |
-| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-manylinux_riscv64<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l<br/>cp39-musllinux_riscv64           |                                                                                                   |                      |
-| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 |                                                                                                   |                      |
-| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                                                                   |                      |
-| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                                                                   | cp312-pyodide_wasm32 |
-| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 |
+|               | macOS                                                                  | Windows                                             | Linux Intel                                                                                         | Linux Other                                                                                                                                                                                                                                                                   | Android                                          | iOS                                                                                               | pyodide (WASM)       |
+|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|---------------------------------------------------------------------------------------------------|----------------------|
+| Python 3.8    | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64    | cp38-win_amd64<br/>cp38-win32                       | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686     | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-manylinux_riscv64<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l<br/>cp38-musllinux_riscv64           |                                                  |                                                                                                   |                      |
+| Python 3.9    | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64    | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64    | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686     | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-manylinux_riscv64<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l<br/>cp39-musllinux_riscv64           |                                                  |                                                                                                   |                      |
+| Python 3.10   | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-manylinux_riscv64<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l<br/>cp310-musllinux_riscv64 |                                                  |                                                                                                   |                      |
+| Python 3.11   | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-manylinux_riscv64<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l<br/>cp311-musllinux_riscv64 |                                                  |                                                                                                   |                      |
+| Python 3.12   | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-manylinux_armv7l<br/>cp312-manylinux_riscv64<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_riscv64 |                                                  |                                                                                                   | cp312-pyodide_wasm32 |
+| Python 3.13   | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-manylinux_riscv64<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l<br/>cp313-musllinux_riscv64 | cp313-android_arm64_v8a<br/>cp313-android_x86_64 | cp313-ios_arm64_iphoneos<br/>cp313-ios_arm64_iphonesimulator<br/>cp313-ios_x86_64_iphonesimulator | cp313-pyodide_wasm32 |
 | Python 3.14   | cp314-macosx_x86_64<br/>cp314-macosx_universal2<br/>cp314-macosx_arm64 | cp314-win_amd64<br/>cp314-win32<br/>cp314-win_arm64 | cp314-manylinux_x86_64<br/>cp314-manylinux_i686<br/>cp314-musllinux_x86_64<br/>cp314-musllinux_i686 | cp314-manylinux_aarch64<br/>cp314-manylinux_ppc64le<br/>cp314-manylinux_s390x<br/>cp314-manylinux_armv7l<br/>cp314-manylinux_riscv64<br/>cp314-musllinux_aarch64<br/>cp314-musllinux_ppc64le<br/>cp314-musllinux_s390x<br/>cp314-musllinux_armv7l<br/>cp314-musllinux_riscv64 | cp314-ios_arm64_iphoneos<br/>cp314-ios_arm64_iphonesimulator<br/>cp314-ios_x86_64_iphonesimulator |                      |
-| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
-| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                                                                   |                      |
-| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
-| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
+| PyPy3.8 v7.3  | pp38-macosx_x86_64<br/>pp38-macosx_arm64                               | pp38-win_amd64                                      | pp38-manylinux_x86_64<br/>pp38-manylinux_i686                                                       | pp38-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                  |                                                                                                   |                      |
+| PyPy3.9 v7.3  | pp39-macosx_x86_64<br/>pp39-macosx_arm64                               | pp39-win_amd64                                      | pp39-manylinux_x86_64<br/>pp39-manylinux_i686                                                       | pp39-manylinux_aarch64                                                                                                                                                                                                                                                        |                                                  |                                                                                                   |                      |
+| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64                             | pp310-win_amd64                                     | pp310-manylinux_x86_64<br/>pp310-manylinux_i686                                                     | pp310-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                  |                                                                                                   |                      |
+| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64                             | pp311-win_amd64                                     | pp311-manylinux_x86_64<br/>pp311-manylinux_i686                                                     | pp311-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                  |                                                                                                   |                      |
 | GraalPy 3.11 v24.2 | gp311_242-macosx_x86_64<br/>gp311_242-macosx_arm64                             | gp311_242-win_amd64                                     | gp311_242-manylinux_x86_64                                                                              | gp311_242-manylinux_aarch64                                                                                                                                                                                                                                                       |                                                                                                   |                      |
 
 The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
@@ -190,6 +191,7 @@ Options:
 - macOS: `x86_64` `arm64` `universal2`
 - Windows: `AMD64` `x86` `ARM64`
 - Pyodide: `wasm32`
+- Android: `arm64_v8a` `x86_64`
 - iOS: `arm64_iphoneos` `arm64_iphonesimulator` `x86_64_iphonesimulator`
 - `auto`: The recommended archs for your machine - see the table below.
 - `auto64`: The 64-bit arch(s) supported by your machine (includes device and simulator for iOS)
@@ -237,7 +239,7 @@ If not listed above, `auto` is the same as `native`.
 [binfmt]: https://hub.docker.com/r/tonistiigi/binfmt
 
 Platform-specific environment variables are also available:<br/>
- `CIBW_ARCHS_MACOS` | `CIBW_ARCHS_WINDOWS` | `CIBW_ARCHS_LINUX` | `CIBW_ARCHS_IOS`
+`CIBW_ARCHS_MACOS` | `CIBW_ARCHS_WINDOWS` | `CIBW_ARCHS_LINUX` | `CIBW_ARCHS_ANDROID` | `CIBW_ARCHS_IOS`
 
 This option can also be set using the [command-line option](#command-line)
 `--archs`. This option cannot be set in an `overrides` section in `pyproject.toml`.
@@ -472,11 +474,11 @@ possible, both through `--installer=uv` passed to build, as well as when making
 all build and test environments. This will generally speed up cibuildwheel.
 Make sure you have an external uv on Windows and macOS, either by
 pre-installing it, or installing cibuildwheel with the uv extra,
-`cibuildwheel[uv]`. You cannot use uv currently on Windows for ARM, for
-musllinux on s390x, or for iOS, as binaries are not provided by uv. Legacy dependencies like
+`cibuildwheel[uv]`. uv currently does not support Windows on ARM,
+musllinux on s390x, Android, or iOS. Legacy dependencies like
 setuptools on Python < 3.12 and pip are not installed if using uv.
 
-Pyodide ignores this setting, as only "build" is supported.
+On Android and Pyodide, only "build" is supported.
 
 You can specify extra arguments to pass to the build frontend using the
 optional `args` option.
@@ -540,7 +542,7 @@ a table of items, including arrays.
     single values.
 
 Platform-specific environment variables also available:<br/>
-`CIBW_CONFIG_SETTINGS_MACOS` | `CIBW_CONFIG_SETTINGS_WINDOWS` | `CIBW_CONFIG_SETTINGS_LINUX` | `CIBW_CONFIG_SETTINGS_IOS` | `CIBW_CONFIG_SETTINGS_PYODIDE`
+`CIBW_CONFIG_SETTINGS_MACOS` | `CIBW_CONFIG_SETTINGS_WINDOWS` | `CIBW_CONFIG_SETTINGS_LINUX` | `CIBW_CONFIG_SETTINGS_ANDROID` | `CIBW_CONFIG_SETTINGS_IOS` | `CIBW_CONFIG_SETTINGS_PYODIDE`
 
 
 #### Examples
@@ -573,7 +575,7 @@ You can use `$PATH` syntax to insert other variables, or the `$(pwd)` syntax to
 To specify more than one environment variable, separate the assignments by spaces.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX` | `CIBW_ENVIRONMENT_IOS` | `CIBW_ENVIRONMENT_PYODIDE`
+`CIBW_ENVIRONMENT_MACOS` | `CIBW_ENVIRONMENT_WINDOWS` | `CIBW_ENVIRONMENT_LINUX` | `CIBW_ENVIRONMENT_ANDROID` | `CIBW_ENVIRONMENT_IOS` | `CIBW_ENVIRONMENT_PYODIDE`
 
 #### Examples
 
@@ -705,7 +707,7 @@ On linux, overriding it triggers a new container launch. It cannot be overridden
 on macOS and Windows.
 
 Platform-specific environment variables also available:<br/>
-`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX` | `CIBW_BEFORE_ALL_IOS` | `CIBW_BEFORE_ALL_PYODIDE`
+`CIBW_BEFORE_ALL_MACOS` | `CIBW_BEFORE_ALL_WINDOWS` | `CIBW_BEFORE_ALL_LINUX` | `CIBW_BEFORE_ALL_ANDROID` | `CIBW_BEFORE_ALL_IOS` | `CIBW_BEFORE_ALL_PYODIDE`
 
 !!! note
 
@@ -771,7 +773,7 @@ The active Python binary can be accessed using `python`, and pip with `pip`; cib
 The command is run in a shell, so you can write things like `cmd1 && cmd2`.
 
 Platform-specific environment variables are also available:<br/>
- `CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX` | `CIBW_BEFORE_BUILD_IOS` | `CIBW_BEFORE_BUILD_PYODIDE`
+ `CIBW_BEFORE_BUILD_MACOS` | `CIBW_BEFORE_BUILD_WINDOWS` | `CIBW_BEFORE_BUILD_LINUX` | `CIBW_BEFORE_BUILD_ANDROID` | `CIBW_BEFORE_BUILD_IOS` | `CIBW_BEFORE_BUILD_PYODIDE`
 
 #### Examples
 
@@ -889,9 +891,9 @@ Default:
 
 - on Linux: `'auditwheel repair -w {dest_dir} {wheel}'`
 - on macOS: `'delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}'`
-- on Windows: `''`
-- on iOS: `''`
-- on Pyodide: `''`
+- on Android: There is no default command, but cibuildwheel will add `libc++` to the
+  wheel if anything links against it. Setting a command will replace this behavior.
+- on other platforms: `''`
 
 A shell command to repair a built wheel by copying external library dependencies into the wheel tree and relinking them.
 The command is run on each built wheel (except for pure Python ones) before testing it.
@@ -905,7 +907,7 @@ The following placeholders must be used inside the command and will be replaced
 The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX` | `CIBW_REPAIR_WHEEL_COMMAND_IOS` | `CIBW_REPAIR_WHEEL_COMMAND_PYODIDE`
+`CIBW_REPAIR_WHEEL_COMMAND_MACOS` | `CIBW_REPAIR_WHEEL_COMMAND_WINDOWS` | `CIBW_REPAIR_WHEEL_COMMAND_LINUX` | `CIBW_REPAIR_WHEEL_COMMAND_ANDROID` | `CIBW_REPAIR_WHEEL_COMMAND_IOS` | `CIBW_REPAIR_WHEEL_COMMAND_PYODIDE`
 
 !!! tip
     cibuildwheel doesn't yet ship a default repair command for Windows.
@@ -1207,7 +1209,7 @@ specifiers inline with the `packages: SPECIFIER...` syntax.
     `./constraints.txt` if that's not found.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS` | `CIBW_DEPENDENCY_VERSIONS_IOS` | `CIBW_DEPENDENCY_VERSIONS_PYODIDE`
+`CIBW_DEPENDENCY_VERSIONS_MACOS` | `CIBW_DEPENDENCY_VERSIONS_WINDOWS` | `CIBW_DEPENDENCY_VERSIONS_ANDROID` | `CIBW_DEPENDENCY_VERSIONS_IOS` | `CIBW_DEPENDENCY_VERSIONS_PYODIDE`
 
 !!! note
     This option does not affect the tools used on the Linux build - those versions
@@ -1318,8 +1320,8 @@ Shell command to run tests after the build. The wheel will be installed
 automatically and available for import from the tests. If this variable is not
 set, your wheel will not be installed after building.
 
-To ensure the wheel is imported by your tests (instead of your source copy),
-**Tests are executed from a temporary directory**, outside of your source
+To ensure your tests import the wheel (instead of your source tree),
+**tests are executed from a temporary working directory**, outside of your source
 tree. To access your test code, you have a couple of options:
 
 - You can use the [`test-sources`](#test-sources) setting to copy specific
@@ -1336,14 +1338,20 @@ tree. To access your test code, you have a couple of options:
     - `{project}` is an absolute path to the project root - the working
       directory where cibuildwheel was called.
 
-On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
+    These placeholders are not available on Android and iOS, because those
+    platforms run tests in a virtual machine that does not have access to
+    the build machine's filesystem.
 
-On iOS, the value of the `test-command` setting must follow the format `python
--m MODULE [ARGS...]` - where MODULE is a Python module name, followed by
-arguments that will be assigned to `sys.argv`. Other commands cannot be used.
+On all platforms other than Android and iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`.
+
+On Android and iOS, the command is parsed by `shlex.split`, and is required to
+be in one of the following forms:
+
+* `python -c command ...` (Android only)
+* `python -m module-name ...`
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE`
+`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_ANDROID` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE`
 
 #### Examples
 
@@ -1401,7 +1409,7 @@ The active Python binary can be accessed using `python`, and pip with `pip`; cib
 The command is run in a shell, so you can write things like `cmd1 && cmd2`.
 
 Platform-specific environment variables are also available:<br/>
- `CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX` | `CIBW_BEFORE_TEST_IOS` | `CIBW_BEFORE_TEST_PYODIDE`
+ `CIBW_BEFORE_TEST_MACOS` | `CIBW_BEFORE_TEST_WINDOWS` | `CIBW_BEFORE_TEST_LINUX` | `CIBW_BEFORE_TEST_ANDROID` | `CIBW_BEFORE_TEST_IOS` | `CIBW_BEFORE_TEST_PYODIDE`
 
 #### Examples
 
@@ -1456,20 +1464,15 @@ Platform-specific environment variables are also available:<br/>
 
 
 ### `test-sources` {: #test-sources env-var toml}
-> Files and folders from the source tree that are copied into an isolated tree before running the tests
+> Paths that are copied into the working directory of the tests
 
 A space-separated list of files and folders, relative to the root of the
 project, required for running the tests. If specified, these files and folders
-will be copied into a temporary folder, and that temporary folder will be used
-as the working directory for running the test suite.
-
-The use of `test-sources` is *required* for iOS builds. This is because the
-simulator does not have access to the project directory, as it is not stored on
-the simulator device. On iOS, the files will be copied into the test application,
-rather than a temporary folder.
+will be copied into the temporary folder which is used as the working directory
+for running the test suite. For more details, see [`test-command`](#test-command).
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_SOURCES_MACOS` | `CIBW_TEST_SOURCES_WINDOWS` | `CIBW_TEST_SOURCES_LINUX` | `CIBW_TEST_SOURCES_IOS` | `CIBW_TEST_SOURCES_PYODIDE`
+`CIBW_TEST_SOURCES_MACOS` | `CIBW_TEST_SOURCES_WINDOWS` | `CIBW_TEST_SOURCES_LINUX` | `CIBW_TEST_SOURCES_ANDROID` | `CIBW_TEST_SOURCES_IOS` | `CIBW_TEST_SOURCES_PYODIDE`
 
 #### Examples
 
@@ -1497,7 +1500,7 @@ Platform-specific environment variables are also available:<br/>
 Space-separated list of dependencies required for running the tests.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX` | `CIBW_TEST_REQUIRES_IOS` | `CIBW_TEST_REQUIRES_PYODIDE`
+`CIBW_TEST_REQUIRES_MACOS` | `CIBW_TEST_REQUIRES_WINDOWS` | `CIBW_TEST_REQUIRES_LINUX` | `CIBW_TEST_REQUIRES_ANDROID` | `CIBW_TEST_REQUIRES_IOS` | `CIBW_TEST_REQUIRES_PYODIDE`
 
 #### Examples
 
@@ -1538,7 +1541,7 @@ tests. This can be used to avoid having to redefine test dependencies in
 `setup.cfg` or `setup.py`.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX` | `CIBW_TEST_EXTRAS_IOS` | `CIBW_TEST_EXTRAS_PYODIDE`
+`CIBW_TEST_EXTRAS_MACOS` | `CIBW_TEST_EXTRAS_WINDOWS` | `CIBW_TEST_EXTRAS_LINUX` | `CIBW_TEST_EXTRAS_ANDROID` | `CIBW_TEST_EXTRAS_IOS` | `CIBW_TEST_EXTRAS_PYODIDE`
 
 #### Examples
 
@@ -1639,7 +1642,7 @@ A space-separated list of environment variables to set in the test environment.
 The syntax is the same as for [`environment`](#environment).
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_TEST_ENVIRONMENT_MACOS` | `CIBW_TEST_ENVIRONMENT_WINDOWS` | `CIBW_TEST_ENVIRONMENT_LINUX` | `CIBW_TEST_ENVIRONMENT_IOS` | `CIBW_TEST_ENVIRONMENT_PYODIDE`
+`CIBW_TEST_ENVIRONMENT_MACOS` | `CIBW_TEST_ENVIRONMENT_WINDOWS` | `CIBW_TEST_ENVIRONMENT_LINUX` | `CIBW_TEST_ENVIRONMENT_ANDROID` |`CIBW_TEST_ENVIRONMENT_IOS` | `CIBW_TEST_ENVIRONMENT_PYODIDE`
 
 #### Examples
 
@@ -1722,7 +1725,7 @@ The default build frontend is `build`, which does show build backend output by
 default.
 
 Platform-specific environment variables are also available:<br/>
-`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX` | `CIBW_BUILD_VERBOSITY_IOS` | `CIBW_BUILD_VERBOSITY_PYODIDE`
+`CIBW_BUILD_VERBOSITY_MACOS` | `CIBW_BUILD_VERBOSITY_WINDOWS` | `CIBW_BUILD_VERBOSITY_LINUX` | `CIBW_BUILD_VERBOSITY_ANDROID` | `CIBW_BUILD_VERBOSITY_IOS` | `CIBW_BUILD_VERBOSITY_PYODIDE`
 
 #### Examples
 
diff --git a/docs/platforms.md b/docs/platforms.md
index 17cc40a88..d67da7a8c 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -178,6 +178,79 @@ If there are pre-releases available for a newer Pyodide version, the `pyodide-pr
 
 Currently, it's recommended to run tests using a `python -m` entrypoint, rather than a command line entrypoint, or a shell script. This is because custom entrypoints have some issues in the Pyodide virtual environment. For example, `pytest` may not work as a command line entrypoint, but will work as a `python -m pytest` entrypoint.
 
+
+## Android {: android}
+
+### Prerequisites
+
+cibuildwheel can build Android wheels on any POSIX platform supported by the Android
+development tools, which currently means Linux x86_64, macOS ARM64 or macOS x86_64. Any
+of these platforms can be used to build wheels for any Android architecture supported by
+Python. However, *testing* wheels has additional requirements: see the section below.
+
+If you already have an Android SDK, export the `ANDROID_HOME` environment variable to
+point at its location. Otherwise, here's how to install it:
+
+* Download the "Command line tools" from <https://developer.android.com/studio>.
+* Create a directory `android-sdk/cmdline-tools`, and unzip the command line
+  tools package into it.
+* Rename `android-sdk/cmdline-tools/cmdline-tools` to
+  `android-sdk/cmdline-tools/latest`.
+* `export ANDROID_HOME=/path/to/android-sdk`
+
+cibuildwheel will automatically use the SDK's `sdkmanager` to install any packages it
+needs.
+
+It also requires the following commands to be on the `PATH`:
+
+* `curl`
+* `java` (or set the `JAVA_HOME` environment variable)
+* `patchelf` (if the wheel links against any external libraries)
+
+### Android version compatibility
+
+Android builds will honor the `ANDROID_API_LEVEL` environment variable to set the
+minimum supported [API level](https://developer.android.com/tools/releases/platforms)
+for generated wheels. This will default to the minimum API level of the selected Python
+version.
+
+### Build frontend support
+
+Android builds only support the `build` frontend. In principle, support for the
+`build[uv]` frontend should be possible, but `uv` [doesn't currently have support for
+cross-platform builds](https://github.com/astral-sh/uv/issues/7957), and [doesn't have
+support for iOS or Android wheel tags](https://github.com/astral-sh/uv/issues/8029).
+
+### Tests
+
+Tests are executed on a Gradle-managed emulator matching the architecture of the build
+machine – for example, if you're building on an ARM64 machine, then you can test an
+ARM64 wheel. Wheels of other architectures can still be built, but testing will
+automatically be skipped.
+
+Running an emulator requires the build machine to either be bare-metal or support
+nested virtualization. CI platforms known to meet this requirement are:
+
+* GitHub Actions Linux x86_64
+
+On Linux, the emulator needs access to the KVM virtualization interface. This may
+require adding your user to a group, or [changing your udev
+rules](https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/).
+If the emulator fails to start, try running `$ANDROID_HOME/emulator/emulator
+-accel-check`.
+
+The Android test environment can't support running shell scripts, so the
+[`test-command`](options.md#test-command) must be a Python command – see its
+documentation for details.
+
+If your package has dependencies which haven't been released on PyPI yet, you may want
+to use the [`environment`](options.md#environment) option to set `PIP_EXTRA_INDEX_URL`
+to one of the following URLs:
+
+* https://chaquo.com/pypi-13.1
+* https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
+
+
 ## iOS {: #ios}
 
 ### System requirements
@@ -241,6 +314,6 @@ If your project requires additional tools to build (such as `cmake`, `ninja`, or
 
 If tests have been configured, the test suite will be executed on the simulator matching the architecture of the build machine - that is, if you're building on an ARM64 macOS machine, the ARM64 wheel will be tested on an ARM64 simulator. It is not possible to use cibuildwheel to test wheels on other simulators, or on physical devices.
 
-The iOS test environment can't support running shell scripts, so the [`test-command`](options.md#test-command) value must be specified as if it were a command line being passed to `python -m ...`. In addition, the project must use [`test-sources`](options.md#test-sources) to specify the minimum subset of files that should be copied to the test environment. This is because the test must be run "on device", and the simulator device will not have access to the local project directory.
+The iOS test environment can't support running shell scripts, so the [`test-command`](options.md#test-command) value must be specified as if it were a command line being passed to `python -m ...`.
 
 The test process uses the same testbed used by CPython itself to run the CPython test suite. It is an Xcode project that has been configured to have a single Xcode "XCUnit" test - the result of which reports the success or failure of running `python -m <test-command>`.
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 8713fee90..134020bc5 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -31,6 +31,12 @@ jobs:
           - os: macos-arm
             # macos-14+ (including latest) are ARM64 runners
             runs-on: macos-latest
+          - os: android-intel
+            runs-on: ubuntu-latest
+            platform: android
+          - os: android-arm
+            runs-on: macos-latest
+            platform: android
           - os: ios
             runs-on: macos-latest
             platform: ios
@@ -41,6 +47,19 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
+      # GitHub Actions can't currently run the Android emulator on macOS.
+      - name: Skip Android tests on macOS
+        if: matrix.os == 'android-arm'
+        run: echo "CIBW_TEST_COMMAND=" >> "$GITHUB_ENV"
+
+      # https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
+      - name: Enable KVM for Android emulator
+        if: matrix.os == 'android-intel'
+        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: Build wheels
         uses: pypa/cibuildwheel@v3.0.1
         env:
diff --git a/pyproject.toml b/pyproject.toml
index 55b254b0f..abe0a92eb 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,12 +39,15 @@ classifiers = [
 dependencies = [
     "bashlex!=0.13",
     "bracex",
+    "build>=1.0.0",
     "certifi",
     "dependency-groups>=1.2",
     "filelock",
     "humanize",
     "packaging>=20.9",
-    "platformdirs"
+    "platformdirs",
+    "pyelftools>=0.29",
+    "wheel>=0.33.6",
 ]
 
 [project.optional-dependencies]
diff --git a/test/test_android.py b/test/test_android.py
new file mode 100644
index 000000000..d176b65bd
--- /dev/null
+++ b/test/test_android.py
@@ -0,0 +1,383 @@
+import os
+import platform
+import re
+from dataclasses import dataclass
+from shutil import rmtree
+from subprocess import CalledProcessError
+from textwrap import dedent
+from zipfile import ZipFile
+
+import pytest
+
+from .test_projects import new_c_project
+from .utils import cibuildwheel_run, expected_wheels
+
+CIBW_PLATFORM = os.environ.get("CIBW_PLATFORM", "android")
+if CIBW_PLATFORM != "android":
+    pytest.skip(f"{CIBW_PLATFORM=}", allow_module_level=True)
+
+if (platform.system(), platform.machine()) not in [
+    ("Linux", "x86_64"),
+    ("Darwin", "arm64"),
+    ("Darwin", "x86_64"),
+]:
+    pytest.skip(
+        f"cibuildwheel does not support building Android wheels on "
+        f"{platform.system()} {platform.machine()}",
+        allow_module_level=True,
+    )
+
+# Detect CI services which have the Android SDK pre-installed.
+ci_supports_build = any(
+    key in os.environ
+    for key in [
+        "GITHUB_ACTIONS",
+        "TF_BUILD",  # Azure Pipelines
+    ]
+)
+
+if "ANDROID_HOME" not in os.environ:
+    msg = "ANDROID_HOME environment variable is not set"
+    if ci_supports_build:
+        pytest.fail(msg)
+    else:
+        pytest.skip(msg, allow_module_level=True)
+
+# Many CI services don't support running the Android emulator: see platforms.md.
+ci_supports_emulator = "GITHUB_ACTIONS" in os.environ and platform.system() == "Linux"
+
+
+def needs_emulator(test):
+    # All copies of the testbed app run on the same emulator with the same
+    # application ID, so these tests must be run serially.
+    test = pytest.mark.serial(test)
+
+    if ci_supports_build and not ci_supports_emulator:
+        test = pytest.mark.skip("This CI platform doesn't support the emulator")(test)
+    return test
+
+
+@dataclass
+class Architecture:
+    linux_machine: str
+    macos_machine: str
+    android_abi: str
+
+
+archs = [
+    Architecture("aarch64", "arm64", "arm64_v8a"),
+    Architecture("x86_64", "x86_64", "x86_64"),
+]
+native_arch = next(
+    arch for arch in archs if platform.machine() in [arch.linux_machine, arch.macos_machine]
+)
+
+
+cp313_env = {
+    "CIBW_PLATFORM": "android",
+    "CIBW_BUILD": "cp313-*",
+}
+
+
+def test_android_home(tmp_path, capfd):
+    new_c_project().generate(tmp_path)
+    env = os.environ.copy()
+    del env["ANDROID_HOME"]
+
+    with pytest.raises(CalledProcessError):
+        cibuildwheel_run(tmp_path, env={**env, **cp313_env})
+    assert "ANDROID_HOME environment variable is not set" in capfd.readouterr().err
+
+
+def test_frontend_good(tmp_path):
+    new_c_project().generate(tmp_path)
+    wheels = cibuildwheel_run(
+        tmp_path,
+        add_env={**cp313_env, "CIBW_BUILD_FRONTEND": "build"},
+    )
+    assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{native_arch.android_abi}.whl"]
+
+
+@pytest.mark.parametrize("frontend", ["build[uv]", "pip"])
+def test_frontend_bad(frontend, tmp_path, capfd):
+    new_c_project().generate(tmp_path)
+    with pytest.raises(CalledProcessError):
+        cibuildwheel_run(
+            tmp_path,
+            add_env={**cp313_env, "CIBW_BUILD_FRONTEND": frontend},
+        )
+    assert "Android requires the build frontend to be 'build'" in capfd.readouterr().err
+
+
+def test_expected_wheels(tmp_path):
+    new_c_project().generate(tmp_path)
+    wheels = cibuildwheel_run(tmp_path, add_env={"CIBW_PLATFORM": "android"})
+    assert wheels == expected_wheels(
+        "spam", "0.1.0", platform="android", machine_arch=native_arch.android_abi
+    )
+
+
+@needs_emulator
+def test_archs(tmp_path, capfd):
+    new_c_project().generate(tmp_path)
+
+    # Build all architectures while checking the handling of the `before` commands.
+    command_pattern = 'echo "Hello from {0}, package={{package}}, python=$(which python)"'
+    output_pattern = (
+        f"Hello from {{0}}, package={tmp_path}, python=/.+/cp313-android_{{1}}/venv/bin/python"
+    )
+
+    wheels = cibuildwheel_run(
+        tmp_path,
+        add_env={
+            **cp313_env,
+            "CIBW_ARCHS": "all",
+            "CIBW_BEFORE_ALL": "echo 'Hello from before_all'",
+            "CIBW_BEFORE_BUILD": command_pattern.format("before_build"),
+            "CIBW_BEFORE_TEST": command_pattern.format("before_test"),
+            "CIBW_TEST_COMMAND": (
+                "python -c 'import platform; print(f\"Hello from {platform.machine()}\")'"
+            ),
+        },
+    )
+    assert wheels == [f"spam-0.1.0-cp313-cp313-android_21_{arch.android_abi}.whl" for arch in archs]
+
+    stdout, stderr = capfd.readouterr()
+    lines = (line for line in stdout.splitlines() if line.startswith("Hello from"))
+    assert next(lines) == "Hello from before_all"
+
+    # All architectures should be built, but only the native architecture should run tests.
+    for arch in archs:
+        abi = arch.android_abi
+        assert re.fullmatch(output_pattern.format("before_build", abi), next(lines))
+        if arch == native_arch:
+            assert re.fullmatch(output_pattern.format("before_test", abi), next(lines))
+            assert next(lines) == f"Hello from {arch.linux_machine}"
+        else:
+            assert (
+                f"Skipping tests for {arch.android_abi}, as the build machine "
+                f"only supports {native_arch.android_abi}"
+            ) in stderr
+
+    try:
+        line = next(lines)
+    except StopIteration:
+        pass
+    else:
+        pytest.fail(f"Unexpected line: {line!r}")
+
+
+def test_build_requires(tmp_path, capfd):
+    # Build-time requirements should be installed for the build platform, not for Android. Prove
+    # this by installing some non-pure-Python requirements and using them in setup.py.
+    #
+    # setup_requires is installed via ProjectBuilder.get_requires_for_build.
+    project = new_c_project(
+        setup_py_setup_args_add="setup_requires=['cmake==3.31.4']",
+        setup_py_add=dedent(
+            """\
+            if "egg_info" not in sys.argv:
+                import subprocess
+                subprocess.run(["cmake", "--version"], check=True)
+
+                from bitarray import bitarray
+                print(f"{bitarray('10110').count()=}")
+            """
+        ),
+    )
+
+    # [build_system] requires is installed via ProjectBuilder.build_system_requires.
+    project.files["pyproject.toml"] = dedent(
+        """\
+        [build-system]
+        requires = ["setuptools", "wheel", "bitarray==3.3.2"]
+        """
+    )
+
+    project.generate(tmp_path)
+    cibuildwheel_run(tmp_path, add_env={**cp313_env})
+
+    # Test for a specific version to minimize the chance that we ran a system cmake.
+    stdout = capfd.readouterr().out
+    assert "cmake version 3.31.4" in stdout
+    assert "bitarray('10110').count()=3" in stdout
+
+
+@pytest.fixture
+def spam_env(tmp_path):
+    project = new_c_project()
+    project.files["test_spam.py"] = dedent(
+        """\
+        import spam
+
+        def test_spam():
+            assert spam.filter("ham")
+            assert not spam.filter("spam")
+            print("Spam test passed")
+        """
+    )
+    project.generate(tmp_path)
+
+    return {
+        **cp313_env,
+        "CIBW_TEST_SOURCES": "test_spam.py",
+        "CIBW_TEST_REQUIRES": "pytest==8.3.5",
+    }
+
+
+@needs_emulator
+@pytest.mark.parametrize(
+    ("command", "expected_output"),
+    [
+        ("python -c 'import test_spam; test_spam.test_spam()'", "Spam test passed"),
+        ("python -m pytest test_spam.py", "=== 1 passed in "),
+        ("pytest test_spam.py", "=== 1 passed in "),
+    ],
+)
+def test_test_command_good(command, expected_output, tmp_path, spam_env, capfd):
+    cibuildwheel_run(tmp_path, add_env={**spam_env, "CIBW_TEST_COMMAND": command})
+    stdout, stderr = capfd.readouterr()
+    assert expected_output in stdout
+
+    if not command.startswith("python"):
+        assert (
+            f"Test command {command!r} is not supported on Android. cibuildwheel "
+            "will try to execute it as if it started with 'python -m'."
+        ) in stderr
+
+
+@needs_emulator
+@pytest.mark.parametrize(
+    ("command", "expected_output"),
+    [
+        # Build-time failure: unrecognized command
+        (
+            "./test_spam.py",
+            "Test command './test_spam.py' is not supported on Android. "
+            "Supported commands are 'python -m' and 'python -c'.",
+        ),
+        # Build-time failure: unrecognized placeholder
+        (
+            "pytest {project}",
+            "Test command 'pytest {project}' with a '{project}' or '{package}' "
+            "placeholder is not supported on Android",
+        ),
+        (
+            "pytest {package}",
+            "Test command 'pytest {package}' with a '{project}' or '{package}' "
+            "placeholder is not supported on Android",
+        ),
+        # Runtime failure
+        ("pytest test_ham.py", "not found: test_ham.py"),
+    ],
+)
+def test_test_command_bad(command, expected_output, tmp_path, spam_env, capfd):
+    with pytest.raises(CalledProcessError):
+        cibuildwheel_run(tmp_path, add_env={**spam_env, "CIBW_TEST_COMMAND": command})
+    assert expected_output in capfd.readouterr().err
+
+
+@needs_emulator
+def test_package_subdir(tmp_path, spam_env, capfd):
+    spam_paths = list(tmp_path.iterdir())
+    package_dir = tmp_path / "package"
+    package_dir.mkdir()
+    for path in spam_paths:
+        path.rename(package_dir / path.name)
+
+    test_filename = "package/" + spam_env["CIBW_TEST_SOURCES"]
+    cibuildwheel_run(
+        tmp_path,
+        package_dir,
+        add_env={
+            **spam_env,
+            "CIBW_TEST_SOURCES": test_filename,
+            "CIBW_TEST_COMMAND": f"python -m pytest {test_filename}",
+        },
+    )
+    assert "=== 1 passed in " in capfd.readouterr().out
+
+
+@needs_emulator
+def test_no_test_sources(tmp_path, capfd):
+    new_c_project().generate(tmp_path)
+    with pytest.raises(CalledProcessError):
+        cibuildwheel_run(
+            tmp_path,
+            add_env={**cp313_env, "CIBW_TEST_COMMAND": "python -m unittest discover"},
+        )
+    assert (
+        "On this platform, you must copy your test files to the testbed app by "
+        "setting the `test-sources` option"
+    ) in capfd.readouterr().err
+
+
+@needs_emulator
+def test_api_level(tmp_path, capfd):
+    project = new_c_project()
+    project.files["pyproject.toml"] = dedent(
+        """\
+        [build-system]
+        requires = ["setuptools"]
+
+        [tool.cibuildwheel]
+        android.environment.ANDROID_API_LEVEL = "33"
+        android.environment.PIP_EXTRA_INDEX_URL = "https://chaquo.com/pypi-13.1"
+        """
+    )
+    project.generate(tmp_path)
+
+    wheels = cibuildwheel_run(
+        tmp_path,
+        add_env={
+            **cp313_env,
+            # Verify that Android dependencies can be installed from the Chaquopy repository, and
+            # that wheels tagged with an older version of Android (in this case 24) are still
+            # accepted.
+            "CIBW_TEST_REQUIRES": "bitarray==3.0.0",
+            "CIBW_TEST_COMMAND": (
+                "python -c 'from bitarray import bitarray; print(~bitarray(\"01100\"))'"
+            ),
+        },
+    )
+    assert wheels == [f"spam-0.1.0-cp313-cp313-android_33_{native_arch.android_abi}.whl"]
+    assert "bitarray('10011')" in capfd.readouterr().out
+
+
+@needs_emulator
+def test_libcxx(tmp_path, capfd):
+    project_dir = tmp_path / "project"
+    output_dir = tmp_path / "output"
+
+    # A C++ package should include libc++, and the extension module should be able to
+    # find it using DT_RUNPATH.
+    new_c_project(setup_py_extension_args_add="language='c++'").generate(project_dir)
+    script = 'import spam; print(", ".join(f"{s}: {spam.filter(s)}" for s in ["ham", "spam"]))'
+    cp313_test_env = {**cp313_env, "CIBW_TEST_COMMAND": f"python -c '{script}'"}
+
+    # Including external libraries requires API level 24.
+    with pytest.raises(CalledProcessError):
+        cibuildwheel_run(project_dir, add_env=cp313_test_env, output_dir=output_dir)
+    assert "libc++_shared.so requires ANDROID_API_LEVEL to be at least 24" in capfd.readouterr().err
+
+    wheels = cibuildwheel_run(
+        project_dir,
+        add_env={**cp313_test_env, "ANDROID_API_LEVEL": "24"},
+        output_dir=output_dir,
+    )
+    assert len(wheels) == 1
+    names = ZipFile(output_dir / wheels[0]).namelist()
+    libcxx_names = [
+        name for name in names if re.fullmatch(r"spam\.libs/libc\+\+_shared-[0-9a-f]{8}\.so", name)
+    ]
+    assert len(libcxx_names) == 1
+    assert "ham: 1, spam: 0" in capfd.readouterr().out
+
+    # A C package should not include libc++.
+    rmtree(project_dir)
+    rmtree(output_dir)
+    new_c_project().generate(project_dir)
+    wheels = cibuildwheel_run(project_dir, add_env=cp313_env, output_dir=output_dir)
+    assert len(wheels) == 1
+    for name in ZipFile(output_dir / wheels[0]).namelist():
+        assert ".libs" not in name
diff --git a/test/test_projects/c.py b/test/test_projects/c.py
index 55d759875..9643e4cda 100644
--- a/test/test_projects/c.py
+++ b/test/test_projects/c.py
@@ -17,7 +17,7 @@
         return NULL;
 
     // Spam should not be allowed through the filter.
-    sts = strcmp(content, "spam");
+    sts = strcmp(content, "spam") != 0;
 
     {{ spam_c_function_add | indent(4) }}
 
diff --git a/test/utils.py b/test/utils.py
index c4310d379..4773ad21c 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -151,7 +151,7 @@ def cibuildwheel_run(
             cwd=project_path,
             check=True,
         )
-        wheels = [p.name for p in (output_dir or Path(tmp_output_dir)).iterdir()]
+        wheels = sorted(p.name for p in (output_dir or Path(tmp_output_dir)).iterdir())
     return wheels
 
 
@@ -262,7 +262,7 @@ def _expected_wheels(
 
     if platform == "pyodide" and python_abi_tags is None:
         python_abi_tags = ["cp312-cp312", "cp313-cp313"]
-    elif platform == "ios" and python_abi_tags is None:
+    elif platform in {"android", "ios"} and python_abi_tags is None:
         python_abi_tags = ["cp313-cp313"]
     elif python_abi_tags is None:
         python_abi_tags = [
@@ -367,6 +367,13 @@ def _expected_wheels(
             if include_universal2:
                 platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2")
 
+        elif platform == "android":
+            api_level = {
+                "cp313-cp313": 21,
+                "cp314-cp314": 24,
+            }[python_abi_tag]
+            platform_tags = [f"android_{api_level}_{machine_arch}"]
+
         elif platform == "ios":
             if machine_arch == "x86_64":
                 platform_tags = [
diff --git a/unit_test/architecture_test.py b/unit_test/architecture_test.py
index 98efe91d8..3033e2be9 100644
--- a/unit_test/architecture_test.py
+++ b/unit_test/architecture_test.py
@@ -5,7 +5,7 @@
 import pytest
 
 import cibuildwheel.architecture
-from cibuildwheel.architecture import Architecture
+from cibuildwheel.architecture import Architecture, arch_synonym
 
 
 @pytest.fixture(
@@ -115,3 +115,20 @@ def test_arch_auto_on_ios(monkeypatch):
     monkeypatch.setattr(platform_module, "machine", lambda: "arm64")
     arch_set = Architecture.parse_config("auto", platform="ios")
     assert arch_set == {Architecture.arm64_iphonesimulator, Architecture.arm64_iphoneos}
+
+
+@pytest.mark.parametrize(
+    ("arch", "from_platform", "to_platform", "expected"),
+    [
+        ("x86_64", "linux", "macos", "x86_64"),
+        ("x86_64", "macos", "linux", "x86_64"),
+        ("x86_64", "linux", "windows", "AMD64"),
+        ("AMD64", "windows", "linux", "x86_64"),
+        ("x86_64", "linux", "nonexistent", "x86_64"),
+        ("x86_64", "nonexistent", "linux", "x86_64"),
+        ("nonexistent", "linux", "windows", "nonexistent"),
+        ("x86", "windows", "macos", None),
+    ],
+)
+def test_arch_synonym(arch, from_platform, to_platform, expected):
+    assert arch_synonym(arch, from_platform, to_platform) == expected
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index cf421ce49..ab6eb4178 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -7,7 +7,7 @@
 
 from cibuildwheel.__main__ import main
 from cibuildwheel.environment import ParsedEnvironment
-from cibuildwheel.frontend import _split_config_settings
+from cibuildwheel.frontend import _split_config_settings, parse_config_settings
 from cibuildwheel.options import BuildOptions, _get_pinned_container_images
 from cibuildwheel.selector import BuildSelector, EnableGroup
 from cibuildwheel.util import resources
@@ -289,7 +289,9 @@ def test_build_verbosity(
 
 @pytest.mark.parametrize("platform_specific", [False, True])
 def test_config_settings(platform_specific, platform, intercepted_build_args, monkeypatch):
-    config_settings = 'setting=value setting=value2 other="something else"'
+    config_settings = (
+        'setting=value setting=value2 triplet=1 triplet=2 triplet=3 other="something else"'
+    )
     if platform_specific:
         monkeypatch.setenv("CIBW_CONFIG_SETTINGS_" + platform.upper(), config_settings)
         monkeypatch.setenv("CIBW_CONFIG_SETTINGS", "a=b")
@@ -304,8 +306,16 @@ def test_config_settings(platform_specific, platform, intercepted_build_args, mo
     assert _split_config_settings(config_settings) == [
         "-Csetting=value",
         "-Csetting=value2",
+        "-Ctriplet=1",
+        "-Ctriplet=2",
+        "-Ctriplet=3",
         "-Cother=something else",
     ]
+    assert parse_config_settings(config_settings) == {
+        "setting": ["value", "value2"],
+        "triplet": ["1", "2", "3"],
+        "other": "something else",
+    }
 
 
 @pytest.mark.parametrize(
@@ -494,7 +504,8 @@ def test_defaults(platform, intercepted_build_args):
     if isinstance(repair_wheel_default, list):
         repair_wheel_default = " && ".join(repair_wheel_default)
     assert build_options.repair_command == repair_wheel_default
-    assert build_options.build_frontend is None
+    assert build_options.build_frontend.name == "build"
+    assert build_options.build_frontend.args == ()
 
     if platform == "linux":
         assert build_options.manylinux_images
diff --git a/unit_test/options_test.py b/unit_test/options_test.py
index d1207aa15..52f2f1adf 100644
--- a/unit_test/options_test.py
+++ b/unit_test/options_test.py
@@ -363,11 +363,11 @@ def test_build_frontend_option(
     parsed_build_frontend = options.build_options(identifier=None).build_frontend
 
     if toml_assignment:
-        assert parsed_build_frontend is not None
         assert parsed_build_frontend.name == result_name
         assert parsed_build_frontend.args == result_args
     else:
-        assert parsed_build_frontend is None
+        assert parsed_build_frontend.name == "build"
+        assert parsed_build_frontend.args == ()
 
 
 def test_override_inherit_environment(tmp_path: Path) -> None:

From f628c9dd23fe6e263cb91cef755a51a0af3bcddc Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 23 Jul 2025 17:18:48 -0400
Subject: [PATCH 165/193] [pre-commit.ci] pre-commit autoupdate (#2505)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.3 → v0.12.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.3...v0.12.4)
- [github.com/pre-commit/mirrors-mypy: v1.16.1 → v1.17.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.1...v1.17.0)

* chore: fix typing issue from match statement

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 .pre-commit-config.yaml      | 4 ++--
 cibuildwheel/projectfiles.py | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index cc9411891..a8c0c3c2f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,14 +14,14 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.12.3
+  rev: v0.12.4
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
   - id: ruff-format
 
 - repo: https://github.com/pre-commit/mirrors-mypy
-  rev: v1.16.1
+  rev: v1.17.0
   hooks:
   - id: mypy
     name: mypy 3.11 on cibuildwheel/
diff --git a/cibuildwheel/projectfiles.py b/cibuildwheel/projectfiles.py
index f9e2ef3b9..196314601 100644
--- a/cibuildwheel/projectfiles.py
+++ b/cibuildwheel/projectfiles.py
@@ -50,7 +50,7 @@ def visit_keyword(self, node: ast.keyword) -> None:
         )
 
         match node:
-            case ast.keyword(arg="python_requires", value=ast.Constant(value=version)):
+            case ast.keyword(arg="python_requires", value=ast.Constant(value=str(version))):
                 if unnested or name_main_unnested:
                     self.requires_python = version
 

From 10c727eed9fc962f75d33d472272e3ad78c3e707 Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Wed, 23 Jul 2025 20:59:02 -0400
Subject: [PATCH 166/193] feat: Python 3.14rc1 build by default (#2507)

---
 README.md                                     |  4 +-
 cibuildwheel/resources/build-platforms.toml   | 24 +++----
 .../resources/pinned_docker_images.cfg        | 66 +++++++++----------
 cibuildwheel/selector.py                      |  2 +-
 pyproject.toml                                |  1 +
 test/utils.py                                 |  5 +-
 unit_test/build_selector_test.py              |  2 +
 unit_test/linux_build_steps_test.py           |  6 +-
 unit_test/option_prepare_test.py              |  6 +-
 9 files changed, 63 insertions(+), 53 deletions(-)

diff --git a/README.md b/README.md
index 6ac4156fc..feaaf5428 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ While cibuildwheel itself requires a recent Python version to run (we support th
 | CPython 3.11       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵ | N/A | N/A | N/A |
 | CPython 3.12       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | N/A | ✅⁴ |
 | CPython 3.13³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | ✅ | ✅ | N/A |
-| CPython 3.14³      | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | N/A | N/A |
+| CPython 3.14       | ✅ | ✅  | ✅  | ✅  | ✅² | ✅ | ✅  | ✅ | ✅  | ✅  | ✅⁵  | N/A | N/A | N/A |
 | PyPy 3.8 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
 | PyPy 3.9 v7.3      | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
 | PyPy 3.10 v7.3     | ✅ | ✅  | ✅  | N/A | N/A | ✅¹ | ✅¹  | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
@@ -40,7 +40,7 @@ While cibuildwheel itself requires a recent Python version to run (we support th
 
 <sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br>
 <sup>² Windows arm64 support is experimental.</sup><br>
-<sup>³ Free-threaded mode requires opt-in using [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</sup><br>
+<sup>³ Free-threaded mode requires opt-in on 3.13 using [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</sup><br>
 <sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br>
 <sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br>
 
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 6f5b7c791..fa0a2fac6 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -165,12 +165,12 @@ python_configurations = [
   { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
   { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
   { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.5/python-3.13.5-macos11.pkg" },
-  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
-  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
-  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
-  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
-  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
-  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0b4-macos11.pkg" },
+  { identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
+  { identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
+  { identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
+  { identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
+  { identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
+  { identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0rc1-macos11.pkg" },
   { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
   { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
   { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
@@ -199,18 +199,18 @@ python_configurations = [
   { identifier = "cp313t-win32", version = "3.13.5", arch = "32" },
   { identifier = "cp313-win_amd64", version = "3.13.5", arch = "64" },
   { identifier = "cp313t-win_amd64", version = "3.13.5", arch = "64" },
-  { identifier = "cp314-win32", version = "3.14.0-b4", arch = "32" },
-  { identifier = "cp314t-win32", version = "3.14.0-b4", arch = "32" },
-  { identifier = "cp314-win_amd64", version = "3.14.0-b4", arch = "64" },
-  { identifier = "cp314t-win_amd64", version = "3.14.0-b4", arch = "64" },
+  { identifier = "cp314-win32", version = "3.14.0-rc1", arch = "32" },
+  { identifier = "cp314t-win32", version = "3.14.0-rc1", arch = "32" },
+  { identifier = "cp314-win_amd64", version = "3.14.0-rc1", arch = "64" },
+  { identifier = "cp314t-win_amd64", version = "3.14.0-rc1", arch = "64" },
   { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
   { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
   { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
   { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
   { identifier = "cp313-win_arm64", version = "3.13.5", arch = "ARM64" },
   { identifier = "cp313t-win_arm64", version = "3.13.5", arch = "ARM64" },
-  { identifier = "cp314-win_arm64", version = "3.14.0-b4", arch = "ARM64" },
-  { identifier = "cp314t-win_arm64", version = "3.14.0-b4", arch = "ARM64" },
+  { identifier = "cp314-win_arm64", version = "3.14.0-rc1", arch = "ARM64" },
+  { identifier = "cp314t-win_arm64", version = "3.14.0-rc1", arch = "ARM64" },
   { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
   { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
   { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index e4bab3e8a..452c58d7b 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,53 +1,53 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.20-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.23-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.20-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.23-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.20-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.23-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.20-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.23-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.20-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.23-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.23-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.23-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.20-2
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.20-2
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.20-2
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.23-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.23-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.23-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.20-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.20-2
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.23-1
 
 [riscv64]
-manylinux_2_39 = quay.io/pypa/manylinux_2_39_riscv64:2025.07.20-2
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_riscv64:2025.07.20-2
+manylinux_2_39 = quay.io/pypa/manylinux_2_39_riscv64:2025.07.23-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_riscv64:2025.07.23-1
 
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 0b986e696..fefa939de 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -87,7 +87,7 @@ def __call__(self, build_id: str) -> bool:
         # filter out groups that are not enabled
         if EnableGroup.CPythonFreeThreading not in self.enable and fnmatch(build_id, "cp313t-*"):
             return False
-        if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp314*"):
+        if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp315*"):
             return False
         if EnableGroup.PyPy not in self.enable and fnmatch(build_id, "pp31*"):
             return False
diff --git a/pyproject.toml b/pyproject.toml
index abe0a92eb..e1101920c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -148,6 +148,7 @@ py-version = "3.11"
 jobs = "0"
 fail-on = ["E", "F"]
 fail-under = "9.8"
+max-positional-arguments = "7"
 reports.output-format = "colorized"
 messages_control.enable = [
   "useless-suppression",
diff --git a/test/utils.py b/test/utils.py
index 4773ad21c..ef8ad81ad 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -272,6 +272,8 @@ def _expected_wheels(
             "cp311-cp311",
             "cp312-cp312",
             "cp313-cp313",
+            "cp314-cp314",
+            "cp314-cp314t",
         ]
 
         enable_groups = get_enable_groups()
@@ -279,8 +281,7 @@ def _expected_wheels(
             python_abi_tags.append("cp313-cp313t")
 
         if EnableGroup.CPythonPrerelease in enable_groups:
-            python_abi_tags.append("cp314-cp314")
-            python_abi_tags.append("cp314-cp314t")
+            ...  # Add cp315 here when available
 
         if EnableGroup.PyPyEoL in enable_groups:
             python_abi_tags += [
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index 4c723244d..2a05f13a0 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -15,6 +15,7 @@ def test_build():
     assert build_selector("cp311-manylinux_x86_64")
     assert build_selector("cp312-manylinux_x86_64")
     assert build_selector("cp313-manylinux_x86_64")
+    assert build_selector("cp314-manylinux_x86_64")
     assert build_selector("pp310-manylinux_x86_64")
     assert build_selector("pp311-manylinux_x86_64")
     assert build_selector("cp36-manylinux_i686")
@@ -36,6 +37,7 @@ def test_build():
     assert build_selector("cp311-win_amd64")
     assert build_selector("cp312-win_amd64")
     assert build_selector("cp313-win_amd64")
+    assert build_selector("cp314-win_amd64")
     assert not build_selector("pp310-win_amd64")
     assert not build_selector("pp311-win_amd64")
 
diff --git a/unit_test/linux_build_steps_test.py b/unit_test/linux_build_steps_test.py
index aa4363c13..0595a6840 100644
--- a/unit_test/linux_build_steps_test.py
+++ b/unit_test/linux_build_steps_test.py
@@ -90,6 +90,8 @@ def container_engines(
         "cp311-manylinux_x86_64",
         "cp312-manylinux_x86_64",
         "cp313-manylinux_x86_64",
+        "cp314-manylinux_x86_64",
+        "cp314t-manylinux_x86_64",
     ]
-    assert before_alls(build_steps[3]) == [""] * 3
-    assert container_engines(build_steps[3]) == [default_container_engine] * 3
+    assert before_alls(build_steps[3]) == [""] * 5
+    assert container_engines(build_steps[3]) == [default_container_engine] * 5
diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py
index 39d6058ab..9412d0855 100644
--- a/unit_test/option_prepare_test.py
+++ b/unit_test/option_prepare_test.py
@@ -13,7 +13,7 @@
 from cibuildwheel.oci_container import OCIPlatform
 from cibuildwheel.util import file
 
-DEFAULT_IDS = {"cp38", "cp39", "cp310", "cp311", "cp312", "cp313"}
+DEFAULT_IDS = {"cp38", "cp39", "cp310", "cp311", "cp312", "cp313", "cp314", "cp314t"}
 ALL_IDS = DEFAULT_IDS | {"cp313t", "pp38", "pp39", "pp310", "pp311", "gp311_242"}
 
 
@@ -155,6 +155,8 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
             "cp312",
             "cp313",
             "cp313t",
+            "cp314",
+            "cp314t",
             "pp38",
             "pp39",
             "pp310",
@@ -177,6 +179,8 @@ def test_build_with_override_launches(monkeypatch, tmp_path):
             "cp312",
             "cp313",
             "cp313t",
+            "cp314",
+            "cp314t",
             "pp38",
             "pp39",
             "pp310",

From 3e2a9aa6e85824999f897fc2c060ca12a5113ef6 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Wed, 23 Jul 2025 22:47:02 -0400
Subject: [PATCH 167/193] fix: regenerate schema

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 bin/generate_schema.py                          | 2 +-
 cibuildwheel/resources/cibuildwheel.schema.json | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/bin/generate_schema.py b/bin/generate_schema.py
index df0bfdf01..ac720e3db 100755
--- a/bin/generate_schema.py
+++ b/bin/generate_schema.py
@@ -63,7 +63,7 @@
     type: string_array
   build-frontend:
     default: default
-    description: Set the tool to use to build, either "pip" (default for now), "build", or "build[uv]"
+    description: Set the tool to use to build, either "build" (default), "build[uv]", or "pip"
     oneOf:
       - enum: [pip, build, "build[uv]", default]
       - type: string
diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json
index 1b00884d3..bae1f9eb5 100644
--- a/cibuildwheel/resources/cibuildwheel.schema.json
+++ b/cibuildwheel/resources/cibuildwheel.schema.json
@@ -1103,6 +1103,12 @@
         "environment": {
           "$ref": "#/properties/environment"
         },
+        "xbuild-tools": {
+          "$ref": "#/properties/xbuild-tools"
+        },
+        "pyodide-version": {
+          "$ref": "#/properties/pyodide-version"
+        },
         "repair-wheel-command": {
           "$ref": "#/properties/repair-wheel-command"
         },
@@ -1120,6 +1126,9 @@
         },
         "test-requires": {
           "$ref": "#/properties/test-requires"
+        },
+        "test-environment": {
+          "$ref": "#/properties/test-environment"
         }
       }
     },

From ffd835cef18fa11522f608fc0fa973b89f5ddc87 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Wed, 23 Jul 2025 23:23:06 -0400
Subject: [PATCH 168/193] Bump version: v3.1.0

---
 README.md                              | 30 ++++++++++++++++++--------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      | 19 ++++++++++++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 65 insertions(+), 34 deletions(-)

diff --git a/README.md b/README.md
index feaaf5428..f8c848667 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.0.1
+        run: python -m pip install cibuildwheel==3.1.0
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -227,6 +227,25 @@ Changelog
 
 <!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
 
+### v3.1.0
+
+_23 July 2025_
+
+
+- 🌟 CPython 3.14 wheels are now built by default - without the `"cpython-prerelease"` `enable` set. It's time to build and upload these wheels to PyPI! This release includes CPython 3.14.0rc1, which is guaranteed to be ABI compatible with the final release. (#2507) Free-threading is no longer experimental in 3.14, so you have to skip it explicitly with `'cp31?t-*'` if you don't support it yet. (#2503)
+- 🌟 Adds the ability to [build wheels for Android](https://cibuildwheel.pypa.io/en/stable/platforms/#android)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `android` on Linux or macOS to try it out! (#2349)
+- 🌟 Adds Pyodide 0.28, which builds 3.13 wheels (#2487)
+- ✨ Support for 32-bit `manylinux_2_28` (now a consistent default) and `manylinux_2_34` added (#2500)
+- 🛠 Improved summary, will also use markdown summary output on GHA (#2469)
+- 🛠 The riscv64 images now have a working default (as they are now part of pypy/manylinux), but are still experimental (and behind an `enable`) since you can't push them to PyPI yet (#2506)
+- 🛠 Fixed a typo in the 3.9 MUSL riscv64 identifier (`cp39-musllinux_ricv64` -> `cp39-musllinux_riscv64`) (#2490)
+- 🛠 Mistyping `--only` now shows the correct possibilities, and even suggests near matches on Python 3.14+ (#2499)
+- 🛠 Only support one output from the repair step on linux like other platforms; auditwheel fixed this over four years ago! (#2478)
+- 🛠 We now use pattern matching extensively (#2434)
+- 📚 We now have platform maintainers for our special platforms and interpreters! (#2481)
+
+
+
 ### v3.0.1
 
 _5 July 2025_
@@ -285,14 +304,7 @@ _24 March 2025_
 - 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
 - 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
 
-### v2.23.1
-
-_15 March 2025_
-
-- ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
-- 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
-
-<!-- [[[end]]] (sum: fBN/s9Yq6D) -->
+<!-- [[[end]]] (sum: HkLT9ZhhtV) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 055276878..f5f41e567 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.0.1"
+__version__ = "3.1.0"
diff --git a/docs/changelog.md b/docs/changelog.md
index 56eaf599e..ee7944484 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,25 @@ title: Changelog
 
 # Changelog
 
+### v3.1.0
+
+_23 July 2025_
+
+
+- 🌟 CPython 3.14 wheels are now built by default - without the `"cpython-prerelease"` `enable` set. It's time to build and upload these wheels to PyPI! This release includes CPython 3.14.0rc1, which is guaranteed to be ABI compatible with the final release. (#2507) Free-threading is no longer experimental in 3.14, so you have to skip it explicitly with `'cp31?t-*'` if you don't support it yet. (#2503)
+- 🌟 Adds the ability to [build wheels for Android](https://cibuildwheel.pypa.io/en/stable/platforms/#android)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `android` on Linux or macOS to try it out! (#2349)
+- 🌟 Adds Pyodide 0.28, which builds 3.13 wheels (#2487)
+- ✨ Support for 32-bit `manylinux_2_28` (now a consistent default) and `manylinux_2_34` added (#2500)
+- 🛠 Improved summary, will also use markdown summary output on GHA (#2469)
+- 🛠 The riscv64 images now have a working default (as they are now part of pypy/manylinux), but are still experimental (and behind an `enable`) since you can't push them to PyPI yet (#2506)
+- 🛠 Fixed a typo in the 3.9 MUSL riscv64 identifier (`cp39-musllinux_ricv64` -> `cp39-musllinux_riscv64`) (#2490)
+- 🛠 Mistyping `--only` now shows the correct possibilities, and even suggests near matches on Python 3.14+ (#2499)
+- 🛠 Only support one output from the repair step on linux like other platforms; auditwheel fixed this over four years ago! (#2478)
+- 🛠 We now use pattern matching extensively (#2434)
+- 📚 We now have platform maintainers for our special platforms and interpreters! (#2481)
+
+
+
 ### v3.0.1
 
 _5 July 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 22db6178b..47bfce2c6 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -110,7 +110,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.0.1
+uses: pypa/cibuildwheel@v3.1.0
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -132,7 +132,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.0.1
+cibuildwheel==3.1.0
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -297,7 +297,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.0.1 --output-dir wheelhouse
+pipx run cibuildwheel==3.1.0 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index aac1c0ef8..9ea5bb1dc 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.0.1
+        pip3 install cibuildwheel==3.1.0
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.0.1
+        python3 -m pip install cibuildwheel==3.1.0
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.0.1
+        pip install cibuildwheel==3.1.0
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index d6c5d9e57..b7d2ac6f2 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.1
+            python3 -m pip install --user cibuildwheel==3.1.0
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.0.1
+            python3 -m pip install --user cibuildwheel==3.1.0
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.0.1
+            pip3 install cibuildwheel==3.1.0
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index f1ff9a889..6aec9ecef 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.1
+    - python -m pip install cibuildwheel==3.1.0
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index c25dadebf..c22d93751 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.0.1
+    - python -m pip install cibuildwheel==3.1.0
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 134020bc5..a344315f0 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -61,7 +61,7 @@ jobs:
           sudo udevadm trigger --name-match=kvm
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.1
+        uses: pypa/cibuildwheel@v3.1.0
         env:
           CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
         # Can also be configured directly, using `with:`
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index 43ecce3eb..dd7553017 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.1
+        uses: pypa/cibuildwheel@v3.1.0
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index f7d769dfa..1b3a50f80 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.0.1
+        run: pipx run cibuildwheel==3.1.0
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 2cace63ae..e239762f8 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.0.1
+        uses: pypa/cibuildwheel@v3.1.0
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index b730d1762..073d3adde 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.0.1
+    - python -m pip install cibuildwheel==3.1.0
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.0.1
+    - py -m pip install cibuildwheel==3.1.0
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.0.1
+    - python3 -m pip install cibuildwheel==3.1.0
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 8c96ed8d7..6b03e8799 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.0.1
+    - python -m pip install cibuildwheel==3.1.0
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index 86ea5bff8..b251ca797 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.1
+  - python3 -m pip install cibuildwheel==3.1.0
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index ab1c90884..4bf8d5e93 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.0.1
+  - python3 -m pip install cibuildwheel==3.1.0
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 042f782f7..76d7ca3e8 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.0.1 twine
+      install: python3 -m pip install cibuildwheel==3.1.0 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.0.1 twine
+      install: python3 -m pip install cibuildwheel==3.1.0 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index e1101920c..c0e887e79 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.0.1"
+version = "3.1.0"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 1ac7fa7f004958fbde774ee89523c446a5d99934 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= <cr52@protonmail.com>
Date: Thu, 24 Jul 2025 18:28:30 +0200
Subject: [PATCH 169/193] fix: report defects in logs and HTML summaries
 (#2512)

* BUG: fix incorrect reporting of number of wheels produced

* BUG: fix incorrect naming of test only sessions in html summaries
---
 cibuildwheel/logger.py        | 8 ++++----
 unit_test/wheel_print_test.py | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index c2b90775d..0f7df826c 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -242,11 +242,11 @@ def print_summary(self, *, options: "Options") -> Generator[None, None, None]:
             github_summary = self._github_step_summary(duration=duration, options=options)
             Path(summary_path).write_text(filter_ansi_codes(github_summary), encoding="utf-8")
 
-        n = len(self.summary)
-        s = "s" if n > 1 else ""
+        n_wheels = len([info for info in self.summary if not info.filename])
+        s = "s" if n_wheels > 1 else ""
         duration_str = humanize.naturaldelta(duration)
         print()
-        self._start_fold_group(f"{n} wheel{s} produced in {duration_str}")
+        self._start_fold_group(f"{n_wheels} wheel{s} produced in {duration_str}")
         for build_info in self.summary:
             print(" ", build_info)
         self._end_fold_group()
@@ -320,7 +320,7 @@ def _github_step_summary(self, duration: float, options: "Options") -> str:
         n_wheels = len([b for b in self.summary if b.filename])
         wheel_rows = "\n".join(
             "<tr>"
-            f"<td nowrap>{'<samp>' + b.filename.name + '</samp>' if b.filename else '*Build only*'}</td>"
+            f"<td nowrap>{'<samp>' + b.filename.name + '</samp>' if b.filename else '*Test only*'}</td>"
             f"<td nowrap>{b.size or 'N/A'}</td>"
             f"<td nowrap><samp>{b.identifier}</samp></td>"
             f"<td nowrap>{humanize.naturaldelta(b.duration)}</td>"
diff --git a/unit_test/wheel_print_test.py b/unit_test/wheel_print_test.py
index 9fd8dfcc5..a7989f668 100644
--- a/unit_test/wheel_print_test.py
+++ b/unit_test/wheel_print_test.py
@@ -25,7 +25,7 @@ def test_printout_wheels(capsys):
 
     assert "id1" in captured.out
     assert "id2" in captured.out
-    assert "wheels produced in" in captured.out
+    assert "1 wheel produced in" in captured.out
     assert "SHA256=" in captured.out
 
 

From 2ca692b1e55a1f924bfb460099c9d7e015671a8d Mon Sep 17 00:00:00 2001
From: timrid <6593626+timrid@users.noreply.github.com>
Date: Thu, 24 Jul 2025 18:29:16 +0200
Subject: [PATCH 170/193] docs: iOS typo fix in docs (#2510)

Update platforms.md

Fixed typo
---
 docs/platforms.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/platforms.md b/docs/platforms.md
index d67da7a8c..fb67c7b38 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -265,7 +265,7 @@ iOS is effectively 2 platforms - physical devices, and simulators. While the API
 
 * `arm64_iphoneos` (for physical iOS devices);
 * `arm64_iphonesimulator` (for iOS simulators running on Apple Silicon macOS machines); and
-* `x64_64_iphonesimulator` (for iOS simulators running on Intel macOS machines).
+* `x86_64_iphonesimulator` (for iOS simulators running on Intel macOS machines).
 
 By default, cibuildwheel will build all wheels for the CPU architecture of the build machine. You can build all wheels for all architectures by specifying `--archs all`.
 

From e6de07ed3921b51089aae6981989889cf1eddd0c Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Thu, 24 Jul 2025 19:14:08 +0100
Subject: [PATCH 171/193] Bump version: v3.1.1

---
 README.md                              | 18 +++++++++---------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      |  7 +++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 41 insertions(+), 34 deletions(-)

diff --git a/README.md b/README.md
index f8c848667..3850f1aae 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.1.0
+        run: python -m pip install cibuildwheel==3.1.1
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -227,6 +227,13 @@ Changelog
 
 <!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
 
+### v3.1.1
+
+_24 July 2025_
+
+- 🐛 Fix a bug showing an incorrect wheel count at the end of execution, and misrepresenting test-only runs in the GitHub Action summary (#2512)
+- 📚 Docs fix (#2510)
+
 ### v3.1.0
 
 _23 July 2025_
@@ -297,14 +304,7 @@ _26 April 2025_
 
 - 🛠 Dependency updates, including Python 3.13.3 (#2371)
 
-### v2.23.2
-
-_24 March 2025_
-
-- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
-- 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
-
-<!-- [[[end]]] (sum: HkLT9ZhhtV) -->
+<!-- [[[end]]] (sum: K97aJ+f0EX) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index f5f41e567..d539d50ce 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.1.0"
+__version__ = "3.1.1"
diff --git a/docs/changelog.md b/docs/changelog.md
index ee7944484..76c7ac9ff 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,13 @@ title: Changelog
 
 # Changelog
 
+### v3.1.1
+
+_24 July 2025_
+
+- 🐛 Fix a bug showing an incorrect wheel count at the end of execution, and misrepresenting test-only runs in the GitHub Action summary (#2512)
+- 📚 Docs fix (#2510)
+
 ### v3.1.0
 
 _23 July 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 47bfce2c6..6aee3bc54 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -110,7 +110,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.1.0
+uses: pypa/cibuildwheel@v3.1.1
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -132,7 +132,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.1.0
+cibuildwheel==3.1.1
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -297,7 +297,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.1.0 --output-dir wheelhouse
+pipx run cibuildwheel==3.1.1 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index 9ea5bb1dc..a9713f230 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.1.0
+        pip3 install cibuildwheel==3.1.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.1.0
+        python3 -m pip install cibuildwheel==3.1.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.1.0
+        pip install cibuildwheel==3.1.1
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index b7d2ac6f2..00f3ad944 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.1.0
+            python3 -m pip install --user cibuildwheel==3.1.1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.1.0
+            python3 -m pip install --user cibuildwheel==3.1.1
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.1.0
+            pip3 install cibuildwheel==3.1.1
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 6aec9ecef..ba81fe597 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.1.0
+    - python -m pip install cibuildwheel==3.1.1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index c22d93751..385d6944b 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.1.0
+    - python -m pip install cibuildwheel==3.1.1
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index a344315f0..905c5f433 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -61,7 +61,7 @@ jobs:
           sudo udevadm trigger --name-match=kvm
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.0
+        uses: pypa/cibuildwheel@v3.1.1
         env:
           CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
         # Can also be configured directly, using `with:`
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index dd7553017..a31954caf 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.0
+        uses: pypa/cibuildwheel@v3.1.1
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index 1b3a50f80..e55d92443 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.1.0
+        run: pipx run cibuildwheel==3.1.1
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index e239762f8..bb6bc9c21 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.0
+        uses: pypa/cibuildwheel@v3.1.1
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 073d3adde..71526af45 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.1.0
+    - python -m pip install cibuildwheel==3.1.1
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.1.0
+    - py -m pip install cibuildwheel==3.1.1
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.1.0
+    - python3 -m pip install cibuildwheel==3.1.1
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 6b03e8799..37c55e4ea 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.1.0
+    - python -m pip install cibuildwheel==3.1.1
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index b251ca797..c7985efbf 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.1.0
+  - python3 -m pip install cibuildwheel==3.1.1
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index 4bf8d5e93..f3f1df5bb 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.1.0
+  - python3 -m pip install cibuildwheel==3.1.1
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index 76d7ca3e8..f72d49c67 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.1.0 twine
+      install: python3 -m pip install cibuildwheel==3.1.1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.1.0 twine
+      install: python3 -m pip install cibuildwheel==3.1.1 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index c0e887e79..d68e7fb98 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.1.0"
+version = "3.1.1"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From 47c5a354212574c63fd7113dc75a537ff9a4bce1 Mon Sep 17 00:00:00 2001
From: Malcolm Smith <smith@chaquo.com>
Date: Thu, 24 Jul 2025 19:51:39 +0100
Subject: [PATCH 172/193] Add Cirrus macOS as a runner that supports building
 for Android but not testing (#2514)

* Add Cirrus macOS as a runner that supports building for Android but not testing

* Try to trigger Cirrus by changing .cirrus.yml
---
 .cirrus.yml          |  4 ++--
 test/test_android.py | 10 ++++------
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/.cirrus.yml b/.cirrus.yml
index 02cc023bb..f8e84294f 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -46,8 +46,8 @@ linux_aarch64_task:
   <<: *RUN_TESTS
 
 windows_x86_task:
-  # The task takes ~55 minutes while the timeout happens
-  # after 60 minutes by default, let's allow some wiggle room.
+  # The task takes ~55 minutes while the timeout happens after 60 minutes by default,
+  # let's allow some wiggle room.
   timeout_in: 120m
   windows_container:
     image: cirrusci/windowsservercore:visualstudio2022
diff --git a/test/test_android.py b/test/test_android.py
index d176b65bd..fb10d23aa 100644
--- a/test/test_android.py
+++ b/test/test_android.py
@@ -28,12 +28,10 @@
     )
 
 # Detect CI services which have the Android SDK pre-installed.
-ci_supports_build = any(
-    key in os.environ
-    for key in [
-        "GITHUB_ACTIONS",
-        "TF_BUILD",  # Azure Pipelines
-    ]
+ci_supports_build = (
+    ("CIRRUS_CI" in os.environ and platform.system() == "Darwin")
+    or "GITHUB_ACTIONS" in os.environ
+    or "TF_BUILD" in os.environ  # Azure Pipelines
 )
 
 if "ANDROID_HOME" not in os.environ:

From 9361af32048a031e9fd10f375746221f8a480308 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Thu, 24 Jul 2025 20:52:07 +0200
Subject: [PATCH 173/193] feat: riscv64 builds are now enabled by default
 (#2509)

PyPI now accepts `riscv64` uploads for manylinux/musllinux.
Those builds do not require an opt-in anymore.
---
 cibuildwheel/selector.py         | 4 ----
 docs/options.md                  | 6 ------
 test/utils.py                    | 2 +-
 unit_test/build_selector_test.py | 9 ---------
 4 files changed, 1 insertion(+), 20 deletions(-)

diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index fefa939de..7ada75e30 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -93,10 +93,6 @@ def __call__(self, build_id: str) -> bool:
             return False
         if EnableGroup.PyPyEoL not in self.enable and fnmatch(build_id, "pp3?-*"):
             return False
-        if EnableGroup.CPythonExperimentalRiscV64 not in self.enable and fnmatch(
-            build_id, "*_riscv64"
-        ):
-            return False
         if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"):
             return False
         # TODO: Re-enable this when we have Pyodide prereleases again (e.g., 0.29.0a1+)
diff --git a/docs/options.md b/docs/options.md
index 735870496..f8d8834ae 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -71,7 +71,6 @@ The list of supported and currently selected build identifiers can also be retri
 The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
 
 Windows arm64 platform support is experimental.
-Linux riscv64 platform support is experimental and requires an explicit opt-in through [`enable`](#enable).
 
 See the [cibuildwheel 2 documentation](https://cibuildwheel.pypa.io/en/2.x/) for past end-of-life versions of Python.
 
@@ -201,8 +200,6 @@ Options:
   to use [`build`](#build-skip) with this option to target specific
   architectures via build selectors.
 
-Linux riscv64 platform support is experimental and requires an explicit opt-in through [`enable`](#enable).
-
 Default: `auto`
 
 | Runner | `native` | `auto` | `auto64` | `auto32` |
@@ -356,9 +353,6 @@ values are:
   free-threaded builds are no longer experimental, so this enable is not needed.
 - `pypy`: Enable PyPy.
 - `pypy-eol`: Enable PyPy versions that have passed end of life (if still available).
-- `cpython-experimental-riscv64`: Enable experimental riscv64 builds. Those builds
-  are disabled by default as they can't be uploaded to PyPI and a PEP will most likely
-  be required before this can happen.
 - `graalpy`: Enable GraalPy.
 - `pyodide-prerelease`: Pyodide versions that haven't released yet, if one is
   available. Safe if you are shipping a site with an early build, not for
diff --git a/test/utils.py b/test/utils.py
index ef8ad81ad..fb45777c9 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -51,7 +51,7 @@ def get_platform() -> str:
         raise Exception(msg)
 
 
-DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease cpython-experimental-riscv64"
+DEFAULT_CIBW_ENABLE = "cpython-freethreading cpython-prerelease"
 
 
 def get_enable_groups() -> frozenset[EnableGroup]:
diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py
index 2a05f13a0..d332ebfd5 100644
--- a/unit_test/build_selector_test.py
+++ b/unit_test/build_selector_test.py
@@ -217,15 +217,6 @@ def test_build_free_threaded_python():
     assert build_selector("cp313t-manylinux_x86_64")
 
 
-def test_build_riscv64_enable():
-    build_selector = BuildSelector(build_config="*", skip_config="")
-    assert not build_selector("cp313-manylinux_riscv64")
-    build_selector = BuildSelector(
-        build_config="*", skip_config="", enable=frozenset([EnableGroup.CPythonExperimentalRiscV64])
-    )
-    assert build_selector("cp313-manylinux_riscv64")
-
-
 def test_testing_selector():
     # This is not a global import to keep pytest from collecting it as a test
     test_selector = cibuildwheel.selector.TestSelector(skip_config="cp36-*")

From 302af028533eb6cf25e89974c96fcc905438bc9f Mon Sep 17 00:00:00 2001
From: Malcolm Smith <smith@chaquo.com>
Date: Thu, 24 Jul 2025 21:38:22 +0100
Subject: [PATCH 174/193] fix(android): execute `wheel` via `sys.executable`
 (#2515)

* Android: execute `wheel` via `sys.executable`

* Suppress unhelpful cyclic-import warning
---
 cibuildwheel/platforms/android.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py
index 52d4898cb..0053933b2 100644
--- a/cibuildwheel/platforms/android.py
+++ b/cibuildwheel/platforms/android.py
@@ -6,6 +6,7 @@
 import shlex
 import shutil
 import subprocess
+import sys
 from collections.abc import Iterable, Iterator
 from dataclasses import dataclass
 from os.path import relpath
@@ -21,7 +22,7 @@
 from elftools.elf.elffile import ELFFile
 from filelock import FileLock
 
-from .. import errors, platforms
+from .. import errors, platforms  # pylint: disable=cyclic-import
 from ..architecture import Architecture, arch_synonym
 from ..frontend import get_build_frontend_extra_flags, parse_config_settings
 from ..logger import log
@@ -517,7 +518,7 @@ def repair_default(
                 f"${{ORIGIN}}/{relpath(libs_dir, path.parent)}",
                 path,
             )
-        call("wheel", "pack", unpacked_dir, "-d", repaired_wheel_dir)
+        call(sys.executable, "-m", "wheel", "pack", unpacked_dir, "-d", repaired_wheel_dir)
 
 
 def elf_file_filter(paths: Iterable[Path]) -> Iterator[tuple[Path, ELFFile]]:

From 13b086e3c6a8a225ee99a028fcf5b820a9d2660f Mon Sep 17 00:00:00 2001
From: tabedzki <35670232+tabedzki@users.noreply.github.com>
Date: Fri, 25 Jul 2025 00:46:16 -0400
Subject: [PATCH 175/193] docs: clarify language around Python requirements for
 local `cibuildwheel` usage (#2513)

* First set of changes to setup.md

* Update docs/setup.md

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>

* Update docs/setup.md

Co-authored-by: Joe Rickerby <joerick@mac.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 docs/setup.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/docs/setup.md b/docs/setup.md
index 03a13491b..412acdf03 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -23,6 +23,8 @@ cibuildwheel
 !!!tip
     You can pass the `--platform linux` option to cibuildwheel to build Linux wheels, even if you're not on Linux. On most machines, the easiest builds to try are the Linux builds. You don't need any software installed except a Docker daemon, such as [Docker Desktop](https://www.docker.com/get-started/). Each platform that cibuildwheel supports has its own system requirements and platform-specific behaviors. See the [platforms page](platforms.md) for details.
 
+    When building locally for macOS or iOS, regardless of the Python you use to invoke cibuildwheel, you'll need [official Python.org](https://www.python.org/downloads/) installations on your machine. `cibuildwheel` won't perform the build using unofficial Python installations such as from `brew` or `uv`.
+
 You should see the builds taking place. You can experiment with [options](options.md) using pyproject.toml or environment variables.
 
 !!! tab "pyproject.toml"

From c89609aaf28a46edbeb6234dca1ed43e31cacb9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= <cr52@protonmail.com>
Date: Sat, 26 Jul 2025 13:13:01 +0200
Subject: [PATCH 176/193] BUG: fix incorrect number of wheels in logs (#2517)

* BUG: fix incorrect number of wheels in logs

* add a test

---------

Co-authored-by: mayeut <mayeut@users.noreply.github.com>
---
 cibuildwheel/logger.py        | 2 +-
 unit_test/wheel_print_test.py | 5 ++++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index 0f7df826c..dad703093 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -242,7 +242,7 @@ def print_summary(self, *, options: "Options") -> Generator[None, None, None]:
             github_summary = self._github_step_summary(duration=duration, options=options)
             Path(summary_path).write_text(filter_ansi_codes(github_summary), encoding="utf-8")
 
-        n_wheels = len([info for info in self.summary if not info.filename])
+        n_wheels = len([info for info in self.summary if info.filename])
         s = "s" if n_wheels > 1 else ""
         duration_str = humanize.naturaldelta(duration)
         print()
diff --git a/unit_test/wheel_print_test.py b/unit_test/wheel_print_test.py
index a7989f668..b7e6953f7 100644
--- a/unit_test/wheel_print_test.py
+++ b/unit_test/wheel_print_test.py
@@ -15,9 +15,11 @@ def test_printout_wheels(capsys):
     log.colors_enabled = False
 
     with log.print_summary(options=OPTIONS_DEFAULTS):
+        # the number of BuildInfo with & without filename shall be different for this test
         log.summary = [
             BuildInfo(identifier="id1", filename=None, duration=3),
             BuildInfo(identifier="id2", filename=FILE, duration=2),
+            BuildInfo(identifier="id3", filename=FILE, duration=3),
         ]
 
     captured = capsys.readouterr()
@@ -25,7 +27,8 @@ def test_printout_wheels(capsys):
 
     assert "id1" in captured.out
     assert "id2" in captured.out
-    assert "1 wheel produced in" in captured.out
+    assert "id3" in captured.out
+    assert "2 wheels produced in" in captured.out
     assert "SHA256=" in captured.out
 
 

From 55fcb25edccf1846b8bcfcef886af5ce19d0b8f2 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sat, 26 Jul 2025 13:09:26 -0400
Subject: [PATCH 177/193] fix: error on CIBW_FREE_THREADED_SUPPORT set (#2520)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 cibuildwheel/__main__.py                  | 53 ++++++++++++++---------
 unit_test/main_tests/main_options_test.py | 12 +++++
 2 files changed, 44 insertions(+), 21 deletions(-)

diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index cdec170d7..b30fab7f8 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -9,7 +9,7 @@
 import textwrap
 import traceback
 import typing
-from collections.abc import Iterable, Sequence
+from collections.abc import Generator, Iterable, Sequence
 from pathlib import Path
 from tempfile import mkdtemp
 from typing import Any, Literal, TextIO
@@ -369,37 +369,50 @@ def print_preamble(platform: str, options: Options, identifiers: Sequence[str])
     print(f"Cache folder: {CIBW_CACHE_PATH}")
     print()
 
-    warnings = detect_warnings(options=options, identifiers=identifiers)
+    warnings = detect_warnings(options=options)
     for warning in warnings:
         log.warning(warning)
 
-    print("Here we go!\n")
+    error_list = list(detect_errors(options=options, identifiers=identifiers))
+    if error_list:
+        for error in error_list:
+            log.error(error)
+        msg = "\n".join(error_list)
+        raise errors.ConfigurationError(msg)
 
+    print("Here we go!\n")
 
-def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]:
-    warnings = []
 
-    python_version_deprecation = ((3, 11), 3)
-    if sys.version_info[:2] < python_version_deprecation[0]:
-        python_version = ".".join(map(str, python_version_deprecation[0]))
-        msg = (
-            f"cibuildwheel {python_version_deprecation[1]} will require Python {python_version}+, "
-            "please upgrade the Python version used to run cibuildwheel. "
-            "This does not affect the versions you can target when building wheels. See: https://cibuildwheel.pypa.io/en/stable/#what-does-it-do"
+def detect_errors(*, options: Options, identifiers: Iterable[str]) -> Generator[str, None, None]:
+    # Check for deprecated CIBW_FREE_THREADED_SUPPORT environment variable
+    if "CIBW_FREE_THREADED_SUPPORT" in os.environ:
+        yield (
+            "CIBW_FREE_THREADED_SUPPORT environment variable is no longer supported. "
+            'Use tool.cibuildwheel.enable = ["cpython-freethreading"] in pyproject.toml '
+            "or set CIBW_ENABLE=cpython-freethreading instead."
         )
-        warnings.append(msg)
 
-    # warn about deprecated {python} and {pip}
+    # Deprecated {python} and {pip}
     for option_name in ["test_command", "before_build"]:
         option_values = [getattr(options.build_options(i), option_name) for i in identifiers]
 
         if any(o and ("{python}" in o or "{pip}" in o) for o in option_values):
             # Reminder: in an f-string, double braces means literal single brace
-            msg = (
+            yield (
                 f"{option_name}: '{{python}}' and '{{pip}}' are no longer supported "
                 "and have been removed in cibuildwheel 3. Simply use 'python' or 'pip' instead."
             )
-            raise errors.ConfigurationError(msg)
+
+
+def detect_warnings(*, options: Options) -> Generator[str, None, None]:
+    python_version_deprecation = ((3, 11), 3)
+    if sys.version_info[:2] < python_version_deprecation[0]:
+        python_version = ".".join(map(str, python_version_deprecation[0]))
+        yield (
+            f"cibuildwheel {python_version_deprecation[1]} will require Python {python_version}+, "
+            "please upgrade the Python version used to run cibuildwheel. "
+            "This does not affect the versions you can target when building wheels. See: https://cibuildwheel.pypa.io/en/stable/#what-does-it-do"
+        )
 
     build_selector = options.globals.build_selector
     test_selector = options.globals.test_selector
@@ -417,27 +430,25 @@ def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str
         identifier for identifier in all_valid_identifiers if enabled_selector(identifier)
     ]
 
-    warnings += check_for_invalid_selectors(
+    yield from check_for_invalid_selectors(
         selector_name="build",
         selector_value=build_selector.build_config,
         all_valid_identifiers=all_valid_identifiers,
         all_enabled_identifiers=all_enabled_identifiers,
     )
-    warnings += check_for_invalid_selectors(
+    yield from check_for_invalid_selectors(
         selector_name="skip",
         selector_value=build_selector.skip_config,
         all_valid_identifiers=all_valid_identifiers,
         all_enabled_identifiers=all_enabled_identifiers,
     )
-    warnings += check_for_invalid_selectors(
+    yield from check_for_invalid_selectors(
         selector_name="test_skip",
         selector_value=test_selector.skip_config,
         all_valid_identifiers=all_valid_identifiers,
         all_enabled_identifiers=all_enabled_identifiers,
     )
 
-    return warnings
-
 
 def check_for_invalid_selectors(
     *,
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index ab6eb4178..32e5728aa 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -16,6 +16,18 @@
 # CIBW_PLATFORM is tested in main_platform_test.py
 
 
+def test_old_free_threaded(monkeypatch, capsys):
+    monkeypatch.setenv("CIBW_FREE_THREADED_SUPPORT", "ON")
+
+    with pytest.raises(SystemExit):
+        main()
+
+    assert (
+        "CIBW_FREE_THREADED_SUPPORT environment variable is no longer supported."
+        in capsys.readouterr().err
+    )
+
+
 @pytest.mark.usefixtures("platform")
 def test_output_dir(intercepted_build_args, monkeypatch):
     OUTPUT_DIR = Path("some_output_dir")

From 140bc1a96a33bde9a11d2a850289cf6683fb3599 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Sun, 27 Jul 2025 06:12:42 -0400
Subject: [PATCH 178/193] tests: mark and add skips (#2524)

* tests: mark and add skips

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix comment

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
---
 .github/workflows/test.yml | 8 +++++---
 pyproject.toml             | 3 +++
 test/test_android.py       | 3 +++
 test/test_ios.py           | 6 ++++++
 test/test_pyodide.py       | 7 +++++++
 5 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c2ec74b99..a95cf2681 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -51,6 +51,7 @@ jobs:
     needs: lint
     runs-on: ${{ matrix.os }}
     strategy:
+      fail-fast: false
       matrix:
         os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-13, macos-15]
         python_version: ['3.13']
@@ -97,6 +98,7 @@ jobs:
         uv sync --no-dev --group test
 
     - uses: joerick/pr-labels-action@v1.0.9
+
     - name: Set CIBW_ENABLE
       shell: bash
       run: |
@@ -134,16 +136,16 @@ jobs:
       with:
         package-dir: sample_proj
         output-dir: wheelhouse_only
-        only: cp312-${{ runner.os == 'Linux' && (runner.arch == 'ARM64' && 'manylinux_aarch64' || 'manylinux_x86_64') || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }}
+        only: cp313-${{ runner.os == 'Linux' && (runner.arch == 'ARM64' && 'manylinux_aarch64' || 'manylinux_x86_64') || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }}
 
     - name: Create custom configuration file
       shell: bash
       run: |
         cat > sample_proj/cibw.toml <<EOF
         [tool.cibuildwheel]
-        # Only build on CPython 3.12 on native arch
+        # Only build on CPython 3.13 on native arch
         archs = ["native"]
-        build = "cp312-*"
+        build = "cp313-*"
         # Skip musllinux
         skip = "*-musllinux*"
         EOF
diff --git a/pyproject.toml b/pyproject.toml
index d68e7fb98..7d5309a47 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -109,6 +109,9 @@ filterwarnings = ["error"]
 log_cli_level = "INFO"
 markers = [
     "serial: tests that must *not* be run in parallel (deselect with '-m \"not serial\"')",
+    "ios: tests that run on iOS",
+    "android: tests that run on Android",
+    "pyodide: tests that run on Pyodide",
 ]
 
 [tool.mypy]
diff --git a/test/test_android.py b/test/test_android.py
index fb10d23aa..e972238b8 100644
--- a/test/test_android.py
+++ b/test/test_android.py
@@ -12,6 +12,9 @@
 from .test_projects import new_c_project
 from .utils import cibuildwheel_run, expected_wheels
 
+pytestmark = pytest.mark.android
+
+
 CIBW_PLATFORM = os.environ.get("CIBW_PLATFORM", "android")
 if CIBW_PLATFORM != "android":
     pytest.skip(f"{CIBW_PLATFORM=}", allow_module_level=True)
diff --git a/test/test_ios.py b/test/test_ios.py
index f727db568..83c529a95 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -12,6 +12,12 @@
 
 from . import test_projects, utils
 
+pytestmark = pytest.mark.ios
+
+CIBW_PLATFORM = os.environ.get("CIBW_PLATFORM", "ios")
+if CIBW_PLATFORM != "ios":
+    pytest.skip(f"{CIBW_PLATFORM=}", allow_module_level=True)
+
 basic_project_files = {
     "tests/test_platform.py": f"""
 import platform
diff --git a/test/test_pyodide.py b/test/test_pyodide.py
index 9240e8266..f126d9fda 100644
--- a/test/test_pyodide.py
+++ b/test/test_pyodide.py
@@ -1,4 +1,5 @@
 import contextlib
+import os
 import subprocess
 import sys
 import textwrap
@@ -9,6 +10,12 @@
 
 from . import test_projects, utils
 
+pytestmark = pytest.mark.pyodide
+
+CIBW_PLATFORM = os.environ.get("CIBW_PLATFORM", "pyodide")
+if CIBW_PLATFORM != "pyodide":
+    pytest.skip(f"{CIBW_PLATFORM=}", allow_module_level=True)
+
 basic_project = test_projects.new_c_project()
 basic_project.files["check_node.py"] = r"""
 import sys

From 1c38fac9c8be640bf8db4083cb27de2cbbbfe334 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Sun, 27 Jul 2025 15:13:38 +0200
Subject: [PATCH 179/193] fix: invalid warning on `test-skip =
 ["*-macosx_universal2:arm64"]` (#2522)

---
 cibuildwheel/__main__.py                  | 22 +++++++++++++++----
 unit_test/main_tests/main_options_test.py | 26 +++++++++++++++++++++++
 2 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index b30fab7f8..0d9914d2a 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -460,17 +460,31 @@ def check_for_invalid_selectors(
     warnings = []
 
     for selector in selector_value.split():
-        if not any(selector_matches(selector, i) for i in all_enabled_identifiers):
+        selector_ = selector
+        if selector_name == "test_skip":
+            # macosx_universal2 uses an additional identifier for tests which ends with ":{arch}"
+            values = selector.split(":")
+            universal2_identifiers = filter(
+                lambda x: x.endswith("-macosx_universal2"), all_valid_identifiers
+            )
+            if len(values) == 2 and any(
+                selector_matches(selector_, f"{i}:{arch}")
+                for i in universal2_identifiers
+                for arch in ["arm64", "x86_64"]
+            ):
+                # just ignore the arch part in the rest of the check
+                selector_ = values[0]
+        if not any(selector_matches(selector_, i) for i in all_enabled_identifiers):
             msg = f"Invalid {selector_name} selector: {selector!r}. "
             error_type: type = errors.ConfigurationError
 
-            if any(selector_matches(selector, i) for i in all_valid_identifiers):
+            if any(selector_matches(selector_, i) for i in all_valid_identifiers):
                 msg += "This selector matches a group that wasn't enabled. Enable it using the `enable` option or remove this selector. "
 
-            if "p2" in selector or "p35" in selector:
+            if "p2" in selector_ or "p35" in selector_:
                 msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update `{selector_name}`. "
                 error_type = errors.DeprecationError
-            if "p36" in selector or "p37" in selector:
+            if "p36" in selector_ or "p37" in selector_:
                 msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update `{selector_name}`. "
                 error_type = errors.DeprecationError
 
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index 32e5728aa..7c8a6dde9 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -105,6 +105,32 @@ def test_invalid_skip_selector(monkeypatch, capsys, option_name, option_env_var)
     assert f"Invalid {option_name} selector" in err
 
 
+@pytest.mark.parametrize(
+    "selector", ["*-macosx_universal2:arm64", "*-macosx_universal2:x86_64", "*-macosx_arm64"]
+)
+@pytest.mark.usefixtures("platform", "intercepted_build_args")
+def test_valid_test_skip_selector(monkeypatch, capsys, selector):
+    monkeypatch.setenv("CIBW_TEST_SKIP", selector)
+
+    main()
+
+    _, err = capsys.readouterr()
+    print(err)
+    assert "Invalid test_skip selector" not in err
+
+
+@pytest.mark.parametrize("selector", ["*-macosx_universal2:invalid", "*-macosx_arm64:arm64"])
+@pytest.mark.usefixtures("platform", "intercepted_build_args")
+def test_invalid_test_skip_selector(monkeypatch, capsys, selector):
+    monkeypatch.setenv("CIBW_TEST_SKIP", selector)
+
+    main()
+
+    _, err = capsys.readouterr()
+    print(err)
+    assert "Invalid test_skip selector" in err
+
+
 @pytest.mark.usefixtures("platform", "intercepted_build_args")
 def test_empty_selector(monkeypatch):
     monkeypatch.setenv("CIBW_SKIP", "*")

From 68c03665d3e9ad24035bc4765aba75a4408c8513 Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Sun, 27 Jul 2025 21:58:03 +0200
Subject: [PATCH 180/193] feat: add deprecation warning for
 `cpython-experimental-riscv64` enable (#2526)

---
 cibuildwheel/__main__.py                  |  7 ++++
 cibuildwheel/options.py                   |  2 +-
 unit_test/main_tests/main_options_test.py | 47 +++++++++++++++++++++++
 3 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index 0d9914d2a..2a4be8833 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -417,6 +417,13 @@ def detect_warnings(*, options: Options) -> Generator[str, None, None]:
     build_selector = options.globals.build_selector
     test_selector = options.globals.test_selector
 
+    if EnableGroup.CPythonExperimentalRiscV64 in build_selector.enable:
+        yield (
+            "'cpython-experimental-riscv64' enable is deprecated and will be removed in a future version. "
+            "It should be removed from tool.cibuildwheel.enable in pyproject.toml "
+            "or CIBW_ENABLE environment variable."
+        )
+
     all_valid_identifiers = [
         config.identifier
         for module in ALL_PLATFORM_MODULES.values()
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 00deb0f8c..6bda5cefd 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -665,7 +665,7 @@ def globals(self) -> GlobalOptions:
             build_config = args.only
             skip_config = ""
             architectures = Architecture.all_archs(self.platform)
-            enable = set(EnableGroup)
+            enable |= set(EnableGroup) - {EnableGroup.CPythonExperimentalRiscV64}
 
         build_selector = BuildSelector(
             build_config=build_config,
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index 7c8a6dde9..c38898819 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -1,3 +1,4 @@
+import os
 import sys
 import tomllib
 from fnmatch import fnmatch
@@ -141,6 +142,52 @@ def test_empty_selector(monkeypatch):
     assert e.value.code == 3
 
 
+@pytest.mark.usefixtures("platform", "intercepted_build_args")
+def test_riscv64_warning1(monkeypatch, capsys):
+    monkeypatch.setenv("CIBW_ENABLE", "cpython-experimental-riscv64")
+
+    main()
+
+    _, err = capsys.readouterr()
+    print(err)
+    assert "'cpython-experimental-riscv64' enable is deprecated" in err
+
+
+@pytest.mark.usefixtures("platform", "intercepted_build_args")
+def test_riscv64_warning2(monkeypatch, capsys, tmp_path):
+    local_path = tmp_path / "tmp_project"
+    os.mkdir(local_path)  # noqa:PTH102 Path.mkdir has been monkeypatched already
+    local_path.joinpath("setup.py").touch()
+
+    monkeypatch.setattr(
+        sys, "argv", ["cibuildwheel", "--only", "cp313-manylinux_riscv64", str(local_path)]
+    )
+    monkeypatch.setenv("CIBW_ENABLE", "cpython-experimental-riscv64")
+
+    main()
+
+    _, err = capsys.readouterr()
+    print(err)
+    assert "'cpython-experimental-riscv64' enable is deprecated" in err
+
+
+@pytest.mark.usefixtures("platform", "intercepted_build_args")
+def test_riscv64_no_warning(monkeypatch, capsys, tmp_path):
+    local_path = tmp_path / "tmp_project"
+    os.mkdir(local_path)  # noqa:PTH102 Path.mkdir has been monkeypatched already
+    local_path.joinpath("setup.py").touch()
+
+    monkeypatch.setattr(
+        sys, "argv", ["cibuildwheel", "--only", "cp313-manylinux_riscv64", str(local_path)]
+    )
+
+    main()
+
+    _, err = capsys.readouterr()
+    print(err)
+    assert "'cpython-experimental-riscv64' enable is deprecated" not in err
+
+
 @pytest.mark.parametrize(
     ("architecture", "image", "full_image"),
     [

From 36c5079ce5f2014f1eaeb7a920a818731e96bb9f Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 28 Jul 2025 07:15:55 +0200
Subject: [PATCH 181/193] tests: reduce testing time on iOS (#2523)

A number of tests only require to run with a single version of python.
This will reduce the time needed to run those tests.
---
 test/test_ios.py | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/test/test_ios.py b/test/test_ios.py
index 83c529a95..061591a42 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -236,6 +236,7 @@ def test_no_xbuild_tool_definition(tmp_path, capfd):
         project_dir,
         add_env={
             "CIBW_PLATFORM": "ios",
+            "CIBW_BUILD": "cp313-*",
             "CIBW_TEST_SKIP": "*",
         },
     )
@@ -245,7 +246,7 @@ def test_no_xbuild_tool_definition(tmp_path, capfd):
         "spam",
         "0.1.0",
         platform="ios",
-        python_abi_tags=["cp313-cp313", "cp314-cp314"],
+        python_abi_tags=["cp313-cp313"],
     )
     assert set(actual_wheels) == set(expected_wheels)
 
@@ -269,13 +270,14 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd):
         project_dir,
         add_env={
             "CIBW_PLATFORM": "ios",
+            "CIBW_BUILD": "cp313-*",
             "CIBW_TEST_SKIP": "*",
             "CIBW_XBUILD_TOOLS": "",
         },
     )
 
     expected_wheels = utils.expected_wheels(
-        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313", "cp314-cp314"]
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
     )
     assert set(actual_wheels) == set(expected_wheels)
 
@@ -305,6 +307,7 @@ def test_spam():
         project_dir,
         add_env={
             "CIBW_PLATFORM": "ios",
+            "CIBW_BUILD": "cp313-*",
             "CIBW_TEST_COMMAND": "pytest ./tests",
             "CIBW_TEST_SOURCES": "tests",
             "CIBW_TEST_REQUIRES": "pytest",
@@ -313,7 +316,7 @@ def test_spam():
     )
 
     expected_wheels = utils.expected_wheels(
-        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313", "cp314-cp314"]
+        "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"]
     )
     assert set(actual_wheels) == set(expected_wheels)
 

From b887417e0ee648b28fa2649b11bae98ed580fccb Mon Sep 17 00:00:00 2001
From: Matthieu Darbois <mayeut@users.noreply.github.com>
Date: Mon, 28 Jul 2025 07:18:35 +0200
Subject: [PATCH 182/193] fix: enable `all`  shall not enable
 `cpython-experimental-riscv64` (#2528)

---
 cibuildwheel/options.py                   |  2 +-
 cibuildwheel/selector.py                  |  2 +-
 unit_test/main_tests/main_options_test.py | 18 ++++++++++++++++++
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 6bda5cefd..eb28d95d0 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -665,7 +665,7 @@ def globals(self) -> GlobalOptions:
             build_config = args.only
             skip_config = ""
             architectures = Architecture.all_archs(self.platform)
-            enable |= set(EnableGroup) - {EnableGroup.CPythonExperimentalRiscV64}
+            enable |= EnableGroup.all_groups()
 
         build_selector = BuildSelector(
             build_config=build_config,
diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py
index 7ada75e30..829e791b5 100644
--- a/cibuildwheel/selector.py
+++ b/cibuildwheel/selector.py
@@ -39,7 +39,7 @@ class EnableGroup(StrEnum):
 
     @classmethod
     def all_groups(cls) -> frozenset["EnableGroup"]:
-        return frozenset(cls)
+        return frozenset(set(cls) - {cls.CPythonExperimentalRiscV64})
 
     @classmethod
     def parse_option_value(cls, value: str) -> frozenset["EnableGroup"]:
diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py
index c38898819..45cbe0562 100644
--- a/unit_test/main_tests/main_options_test.py
+++ b/unit_test/main_tests/main_options_test.py
@@ -188,6 +188,24 @@ def test_riscv64_no_warning(monkeypatch, capsys, tmp_path):
     assert "'cpython-experimental-riscv64' enable is deprecated" not in err
 
 
+@pytest.mark.usefixtures("platform", "intercepted_build_args")
+def test_riscv64_no_warning2(monkeypatch, capsys, tmp_path):
+    local_path = tmp_path / "tmp_project"
+    os.mkdir(local_path)  # noqa:PTH102 Path.mkdir has been monkeypatched already
+    local_path.joinpath("setup.py").touch()
+
+    monkeypatch.setattr(
+        sys, "argv", ["cibuildwheel", "--only", "cp313-manylinux_riscv64", str(local_path)]
+    )
+    monkeypatch.setenv("CIBW_ENABLE", "all")
+
+    main()
+
+    _, err = capsys.readouterr()
+    print(err)
+    assert "'cpython-experimental-riscv64' enable is deprecated" not in err
+
+
 @pytest.mark.parametrize(
     ("architecture", "image", "full_image"),
     [

From 0dc0b48e4dfc7905ac6eed2fcfb344de21fe41ff Mon Sep 17 00:00:00 2001
From: "cibuildwheel-bot[bot]"
 <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Date: Mon, 28 Jul 2025 08:20:49 -0600
Subject: [PATCH 183/193] [Bot] Update dependencies (#2529)

Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
---
 .../resources/constraints-pyodide312.txt      |   2 +-
 .../resources/constraints-pyodide313.txt      |   2 +-
 .../resources/pinned_docker_images.cfg        |  66 +--
 .../python-build-standalone-releases.json     | 450 +++++++++---------
 4 files changed, 260 insertions(+), 260 deletions(-)

diff --git a/cibuildwheel/resources/constraints-pyodide312.txt b/cibuildwheel/resources/constraints-pyodide312.txt
index 88a0628be..12e4b9c6c 100644
--- a/cibuildwheel/resources/constraints-pyodide312.txt
+++ b/cibuildwheel/resources/constraints-pyodide312.txt
@@ -76,7 +76,7 @@ requests==2.32.4
     # via pyodide-build
 resolvelib==1.2.0
     # via pyodide-build
-rich==14.0.0
+rich==14.1.0
     # via
     #   pyodide-build
     #   pyodide-cli
diff --git a/cibuildwheel/resources/constraints-pyodide313.txt b/cibuildwheel/resources/constraints-pyodide313.txt
index b713eee58..e359fe302 100644
--- a/cibuildwheel/resources/constraints-pyodide313.txt
+++ b/cibuildwheel/resources/constraints-pyodide313.txt
@@ -76,7 +76,7 @@ requests==2.32.4
     # via pyodide-build
 resolvelib==1.2.0
     # via pyodide-build
-rich==14.0.0
+rich==14.1.0
     # via
     #   pyodide-build
     #   pyodide-cli
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 452c58d7b..b5d013396 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,53 +1,53 @@
 [x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.27-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.07.27-1
 
 [i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.27-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.07.27-1
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.27-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.07.27-1
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2025.07.27-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.07.27-1
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2025.07.27-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.07.27-1
 
 [pypy_x86_64]
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2025.07.27-1
 
 [pypy_i686]
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2025.07.27-1
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.23-1
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.23-1
-manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.23-1
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.07.27-1
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.07.27-1
+manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2025.07.27-1
 
 [armv7l]
-manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.23-1
+manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.07.27-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.07.27-1
 
 [riscv64]
-manylinux_2_39 = quay.io/pypa/manylinux_2_39_riscv64:2025.07.23-1
-musllinux_1_2 = quay.io/pypa/musllinux_1_2_riscv64:2025.07.23-1
+manylinux_2_39 = quay.io/pypa/manylinux_2_39_riscv64:2025.07.27-1
+musllinux_1_2 = quay.io/pypa/musllinux_1_2_riscv64:2025.07.27-1
 
diff --git a/cibuildwheel/resources/python-build-standalone-releases.json b/cibuildwheel/resources/python-build-standalone-releases.json
index fa09609fa..4ce10ea4a 100644
--- a/cibuildwheel/resources/python-build-standalone-releases.json
+++ b/cibuildwheel/resources/python-build-standalone-releases.json
@@ -1,455 +1,455 @@
 {
   "releases": [
     {
-      "tag": "20250712",
+      "tag": "20250723",
       "assets": [
         {
-          "name": "cpython-3.10.18+20250712-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.10.18+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.10.18%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.10.18+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.10.18%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.11.13+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.11.13%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.11.13+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.11.13%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.12.11+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.12.11%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.12.11+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.12.11%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.13.5+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.13.5%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.13.5+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.13.5%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-aarch64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-aarch64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-aarch64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-aarch64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.14.0b4+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.14.0b4%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.14.0rc1+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.14.0rc1%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-aarch64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-aarch64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-aarch64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-aarch64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-aarch64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-aarch64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-aarch64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-aarch64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-armv7-unknown-linux-gnueabi-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-armv7-unknown-linux-gnueabi-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-i686-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-i686-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-i686-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-i686-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-ppc64le-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-ppc64le-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-riscv64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-riscv64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-riscv64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-riscv64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-s390x-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-s390x-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-s390x-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-s390x-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64-apple-darwin-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-apple-darwin-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64-apple-darwin-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-apple-darwin-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64-pc-windows-msvc-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-pc-windows-msvc-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64-pc-windows-msvc-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-pc-windows-msvc-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
         },
         {
-          "name": "cpython-3.9.23+20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
-          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250712/cpython-3.9.23%2B20250712-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
+          "name": "cpython-3.9.23+20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
+          "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20250723/cpython-3.9.23%2B20250723-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
         }
       ]
     }

From 46a6d279953e2947496fa28a22ded264f4027a5f Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Tue, 29 Jul 2025 02:26:52 +0800
Subject: [PATCH 184/193] Bump iOS support package versions. (#2530)

---
 cibuildwheel/resources/build-platforms.toml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index fa0a2fac6..1f78b8530 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -232,10 +232,10 @@ python_configurations = [
 
 [ios]
 python_configurations = [
-  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
-  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
-  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b9/Python-3.13-iOS-support.b9.tar.gz" },
-  { identifier = "cp314-ios_arm64_iphoneos", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b5/Python-3.14-iOS-support.b5.tar.gz" },
-  { identifier = "cp314-ios_x86_64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b5/Python-3.14-iOS-support.b5.tar.gz" },
-  { identifier = "cp314-ios_arm64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b5/Python-3.14-iOS-support.b5.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b10/Python-3.13-iOS-support.b10.tar.gz" },
+  { identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b10/Python-3.13-iOS-support.b10.tar.gz" },
+  { identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b10/Python-3.13-iOS-support.b10.tar.gz" },
+  { identifier = "cp314-ios_arm64_iphoneos", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b6/Python-3.14-iOS-support.b6.tar.gz" },
+  { identifier = "cp314-ios_x86_64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b6/Python-3.14-iOS-support.b6.tar.gz" },
+  { identifier = "cp314-ios_arm64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b6/Python-3.14-iOS-support.b6.tar.gz" },
 ]

From 1953c0497215dcf2711e1fbfd3ae8952e8ad604c Mon Sep 17 00:00:00 2001
From: Joe Rickerby <joerick@mac.com>
Date: Mon, 28 Jul 2025 20:00:54 +0100
Subject: [PATCH 185/193] Adding @mhsmith as platform maintainer for Android
 (#2516)

---
 .github/CODEOWNERS | 1 +
 README.md          | 1 +
 2 files changed, 2 insertions(+)

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index fb4409e64..3a6f76b93 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,2 +1,3 @@
 cibuildwheel/platforms/ios.py         @freakboy3742
 cibuildwheel/platforms/pyodide.py     @hoodmane @ryanking13 @agriyakhetarpal
+cibuildwheel/platforms/android.py     @mhsmith
diff --git a/README.md b/README.md
index 3850f1aae..bd31b4125 100644
--- a/README.md
+++ b/README.md
@@ -339,6 +339,7 @@ Platform maintainers:
 - Hood Chatham [@hoodmane](https://github.com/hoodmane) (Pyodide)
 - Gyeongjae Choi [@ryanking13](https://github.com/ryanking13) (Pyodide)
 - Tim Felgentreff [@timfel](https://github.com/timfel) (GraalPy)
+- Malcolm Smith [@mhsmith](https://github.com/mhsmith) (Android)
 
 Credits
 -------

From 8ef9414f60b366420233447f0abd96586ed394c7 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 28 Jul 2025 16:06:41 -0600
Subject: [PATCH 186/193] [pre-commit.ci] pre-commit autoupdate (#2532)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.4 → v0.12.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.4...v0.12.5)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
 .pre-commit-config.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a8c0c3c2f..622bc5e69 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.12.4
+  rev: v0.12.5
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]

From 9e4e50bd76b3190f55304387e333f6234823ea9b Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Tue, 29 Jul 2025 09:12:51 -0600
Subject: [PATCH 187/193] Bump version: v3.1.2

---
 README.md                              | 24 ++++++++++++++++--------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      | 14 ++++++++++++++
 docs/faq.md                            |  6 +++---
 examples/azure-pipelines-minimal.yml   |  6 +++---
 examples/circleci-minimal.yml          |  6 +++---
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +++---
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 ++--
 pyproject.toml                         |  2 +-
 18 files changed, 55 insertions(+), 33 deletions(-)

diff --git a/README.md b/README.md
index bd31b4125..d82815d73 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.1.1
+        run: python -m pip install cibuildwheel==3.1.2
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -227,6 +227,20 @@ Changelog
 
 <!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
 
+### v3.1.2
+
+_29 July 2025_
+
+- ⚠️  Add an error if `CIBW_FREE_THREADING_SUPPORT` is set; you are likely missing 3.13t wheels, please use the `enable`/`CIBW_ENABLE` (#2520)
+- 🛠 `riscv64` now enabled if you target that architecture, it's now supported on PyPI (#2509)
+- 🛠 Add warning when using `cpython-experimental-riscv64` (no longer needed) (#2526, #2528)
+- 🛠 iOS versions bumped, fixing issues with 3.14 (now RC 1) (#2530)
+- 🐛 Fix bug in Android running wheel from our GitHub Action (#2517)
+- 🐛 Fix warning when using `test-skip` of `"*-macosx_universal2:arm64"` (#2522)
+- 🐛 Fix incorrect number of wheels reported in logs, again (#2517)
+- 📚 We welcome our Android platform maintainer (#2516)
+
+
 ### v3.1.1
 
 _24 July 2025_
@@ -298,13 +312,7 @@ See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) f
 - 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
 - 📚 README table now matches docs and auto-updates. (#2427, #2428)
 
-### v2.23.3
-
-_26 April 2025_
-
-- 🛠 Dependency updates, including Python 3.13.3 (#2371)
-
-<!-- [[[end]]] (sum: K97aJ+f0EX) -->
+<!-- [[[end]]] (sum: sVC5DNuhaF) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index d539d50ce..911557b86 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.1.1"
+__version__ = "3.1.2"
diff --git a/docs/changelog.md b/docs/changelog.md
index 76c7ac9ff..a0223405b 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,20 @@ title: Changelog
 
 # Changelog
 
+### v3.1.2
+
+_29 July 2025_
+
+- ⚠️  Add an error if `CIBW_FREE_THREADING_SUPPORT` is set; you are likely missing 3.13t wheels, please use the `enable`/`CIBW_ENABLE` (#2520)
+- 🛠 `riscv64` now enabled if you target that architecture, it's now supported on PyPI (#2509)
+- 🛠 Add warning when using `cpython-experimental-riscv64` (no longer needed) (#2526, #2528)
+- 🛠 iOS versions bumped, fixing issues with 3.14 (now RC 1) (#2530)
+- 🐛 Fix bug in Android running wheel from our GitHub Action (#2517)
+- 🐛 Fix warning when using `test-skip` of `"*-macosx_universal2:arm64"` (#2522)
+- 🐛 Fix incorrect number of wheels reported in logs, again (#2517)
+- 📚 We welcome our Android platform maintainer (#2516)
+
+
 ### v3.1.1
 
 _24 July 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 6aee3bc54..6cbc78780 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -110,7 +110,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.1.1
+uses: pypa/cibuildwheel@v3.1.2
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -132,7 +132,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.1.1
+cibuildwheel==3.1.2
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -297,7 +297,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.1.1 --output-dir wheelhouse
+pipx run cibuildwheel==3.1.2 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index a9713f230..df8765af1 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.1.1
+        pip3 install cibuildwheel==3.1.2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.1.1
+        python3 -m pip install cibuildwheel==3.1.2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.1.1
+        pip install cibuildwheel==3.1.2
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 00f3ad944..6e548d6c4 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.1.1
+            python3 -m pip install --user cibuildwheel==3.1.2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.1.1
+            python3 -m pip install --user cibuildwheel==3.1.2
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.1.1
+            pip3 install cibuildwheel==3.1.2
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index ba81fe597..321dc419a 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.1.1
+    - python -m pip install cibuildwheel==3.1.2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index 385d6944b..d18547d4d 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.1.1
+    - python -m pip install cibuildwheel==3.1.2
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index 905c5f433..fdd7ae892 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -61,7 +61,7 @@ jobs:
           sudo udevadm trigger --name-match=kvm
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.1
+        uses: pypa/cibuildwheel@v3.1.2
         env:
           CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
         # Can also be configured directly, using `with:`
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index a31954caf..d2f68b390 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.1
+        uses: pypa/cibuildwheel@v3.1.2
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index e55d92443..2fa452582 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.1.1
+        run: pipx run cibuildwheel==3.1.2
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index bb6bc9c21..39211a273 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.1
+        uses: pypa/cibuildwheel@v3.1.2
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 71526af45..314e1bf96 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.1.1
+    - python -m pip install cibuildwheel==3.1.2
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.1.1
+    - py -m pip install cibuildwheel==3.1.2
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.1.1
+    - python3 -m pip install cibuildwheel==3.1.2
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index 37c55e4ea..c8b175c46 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.1.1
+    - python -m pip install cibuildwheel==3.1.2
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index c7985efbf..d59351707 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.1.1
+  - python3 -m pip install cibuildwheel==3.1.2
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index f3f1df5bb..a7f1ac4bc 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.1.1
+  - python3 -m pip install cibuildwheel==3.1.2
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index f72d49c67..e458565c5 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.1.1 twine
+      install: python3 -m pip install cibuildwheel==3.1.2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.1.1 twine
+      install: python3 -m pip install cibuildwheel==3.1.2 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index 7d5309a47..40b7c3127 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.1.1"
+version = "3.1.2"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"

From e2c7102ed7981cd79d28a5eb0a196f8242b1adab Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <tim.felgentreff@oracle.com>
Date: Thu, 31 Jul 2025 06:31:23 +0200
Subject: [PATCH 188/193] chore: remove some GraalPy Windows workarounds.
 (#2501)

GraalPy made a release where pytest works on Windows
---
 test/test_before_test.py |  4 +---
 test/test_testing.py     | 16 ++++------------
 2 files changed, 5 insertions(+), 15 deletions(-)

diff --git a/test/test_before_test.py b/test/test_before_test.py
index 3c722d089..bf0238492 100644
--- a/test/test_before_test.py
+++ b/test/test_before_test.py
@@ -63,9 +63,7 @@ def test(tmp_path, build_frontend_env):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
-            # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "where graalpy || pytest {project}/test",
+            "CIBW_TEST_COMMAND_WINDOWS": "pytest {project}/test",
             **build_frontend_env,
         },
     )
diff --git a/test/test_testing.py b/test/test_testing.py
index de750eaef..65cb66bae 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -81,9 +81,7 @@ def test(tmp_path):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
-            # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest {project}/test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
         },
     )
 
@@ -104,9 +102,7 @@ def test_extras_require(tmp_path):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
-            # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest {project}/test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
         },
         single_python=True,
     )
@@ -138,9 +134,7 @@ def test_dependency_groups(tmp_path):
             # the 'false ||' bit is to ensure this command runs in a shell on
             # mac/linux.
             "CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
-            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
-            # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || where graalpy || pytest {project}/test",
+            "CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
         },
         single_python=True,
     )
@@ -230,9 +224,7 @@ def test_test_sources(tmp_path):
         project_dir,
         add_env={
             "CIBW_TEST_REQUIRES": "pytest",
-            # pytest fails on GraalPy 24.2.0 on Windows so we skip it there
-            # until https://github.com/oracle/graalpython/issues/490 is fixed
-            "CIBW_TEST_COMMAND_WINDOWS": "where graalpy || pytest",
+            "CIBW_TEST_COMMAND_WINDOWS": "pytest",
             "CIBW_TEST_COMMAND": utils.invoke_pytest(),
             "CIBW_TEST_SOURCES": "test",
         },

From 60b9cc95db51f9f5e48562fcb1b3f7ac3f9cb4a1 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 31 Jul 2025 12:26:07 -0400
Subject: [PATCH 189/193] fix: never call pip directly (#2537)

You can't change the running process in Windows. In pip 25.2, the format of the generated file changes, so this now breaks if you try to update to the latest pip (which can happen if deps are unpinned).
---
 cibuildwheel/venv.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/cibuildwheel/venv.py b/cibuildwheel/venv.py
index d004753c4..e4d3c8720 100644
--- a/cibuildwheel/venv.py
+++ b/cibuildwheel/venv.py
@@ -143,6 +143,8 @@ def virtualenv(
     venv_env["VIRTUAL_ENV"] = str(venv_path)
     if not use_uv and pip_version == "embed":
         call(
+            "python",
+            "-m",
             "pip",
             "install",
             "--upgrade",

From feeb3992a7ea36ffbc9d4446debea40f9aa24861 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Thu, 31 Jul 2025 12:26:17 -0400
Subject: [PATCH 190/193] tests: add flaky test handling (#2527)

* chore: add flaky test handling

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update test_android.py

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 pyproject.toml       | 1 +
 test/test_android.py | 2 ++
 test/test_ios.py     | 2 ++
 3 files changed, 5 insertions(+)

diff --git a/pyproject.toml b/pyproject.toml
index 40b7c3127..f1cd7328b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -88,6 +88,7 @@ test = [
     "jinja2",
     "pytest-timeout",
     "pytest-xdist",
+    "pytest-rerunfailures",
     "pytest>=6",
     "setuptools",
     "tomli_w",
diff --git a/test/test_android.py b/test/test_android.py
index e972238b8..bd9e1a761 100644
--- a/test/test_android.py
+++ b/test/test_android.py
@@ -90,6 +90,8 @@ def test_android_home(tmp_path, capfd):
     assert "ANDROID_HOME environment variable is not set" in capfd.readouterr().err
 
 
+# Can fail to setup
+@pytest.mark.flaky(reruns=2)
 def test_frontend_good(tmp_path):
     new_c_project().generate(tmp_path)
     wheels = cibuildwheel_run(
diff --git a/test/test_ios.py b/test/test_ios.py
index 061591a42..9a41d2d3f 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -49,7 +49,9 @@ def skip_if_ios_testing_not_supported() -> None:
 # it's easy to overload the CI machine if there are multiple test processes
 # running multithreaded processes. Therefore, they're put in the serial group,
 # which is guaranteed to run single-process.
+# This can also fail the first time sometimes.
 @pytest.mark.serial
+@pytest.mark.flaky(reruns=2)
 @pytest.mark.parametrize(
     "build_config",
     [

From 8c5c738023fee8aad6412105b42ea798066b1438 Mon Sep 17 00:00:00 2001
From: Vytautas Liuolia <vytautas.liuolia@gmail.com>
Date: Thu, 31 Jul 2025 18:26:27 +0200
Subject: [PATCH 191/193] docs(project): add Falcon to working examples (#2538)

---
 docs/data/projects.yml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/docs/data/projects.yml b/docs/data/projects.yml
index e6ce488d9..b365de8c1 100644
--- a/docs/data/projects.yml
+++ b/docs/data/projects.yml
@@ -664,3 +664,10 @@
   os: [apple, linux, windows]
   pypi: streaming-form-data
   notes: Streaming parser for multipart/form-data written in Cython
+
+- name: falcon
+  gh: falconry/falcon
+  ci: [github]
+  os: [apple, linux, windows]
+  pypi: falcon
+  notes: Falcon is a no-magic ASGI/WSGI API and microservices framework; it uses cibuildwheel for (optional) Cython extensions.

From c463e56ba22f7f7e6c8871b006a06384c08cff34 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <HenrySchreinerIII@gmail.com>
Date: Fri, 1 Aug 2025 12:32:01 -0400
Subject: [PATCH 192/193] tests: another iOS flaky spot (#2539)

* tests: another iOS flaky spot

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: missing interceptions

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
---
 test/test_ios.py                 | 1 +
 unit_test/main_tests/conftest.py | 6 ++++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/test/test_ios.py b/test/test_ios.py
index 9a41d2d3f..a21cc1cfc 100644
--- a/test/test_ios.py
+++ b/test/test_ios.py
@@ -171,6 +171,7 @@ def test_ios_testing_with_placeholder(tmp_path, capfd):
 
 
 @pytest.mark.serial
+@pytest.mark.flaky(reruns=2)
 def test_ios_test_command_short_circuit(tmp_path, capfd):
     skip_if_ios_testing_not_supported()
 
diff --git a/unit_test/main_tests/conftest.py b/unit_test/main_tests/conftest.py
index ef5c08340..cf92ea1a0 100644
--- a/unit_test/main_tests/conftest.py
+++ b/unit_test/main_tests/conftest.py
@@ -8,7 +8,7 @@
 
 from cibuildwheel import architecture
 from cibuildwheel.logger import Logger
-from cibuildwheel.platforms import linux, macos, pyodide, windows
+from cibuildwheel.platforms import android, ios, linux, macos, pyodide, windows
 from cibuildwheel.util import file
 
 
@@ -87,10 +87,12 @@ def platform(request, monkeypatch):
 def intercepted_build_args(monkeypatch):
     intercepted = ArgsInterceptor()
 
+    monkeypatch.setattr(android, "build", intercepted)
+    monkeypatch.setattr(ios, "build", intercepted)
     monkeypatch.setattr(linux, "build", intercepted)
     monkeypatch.setattr(macos, "build", intercepted)
-    monkeypatch.setattr(windows, "build", intercepted)
     monkeypatch.setattr(pyodide, "build", intercepted)
+    monkeypatch.setattr(windows, "build", intercepted)
 
     yield intercepted
 

From 352e01339f0a173aa2a3eb57f01492e341e83865 Mon Sep 17 00:00:00 2001
From: Henry Schreiner <henryschreineriii@gmail.com>
Date: Fri, 1 Aug 2025 12:38:46 -0400
Subject: [PATCH 193/193] Bump version: v3.1.3

---
 README.md                              | 52 ++++++--------------------
 cibuildwheel/__init__.py               |  2 +-
 docs/changelog.md                      | 10 +++++
 docs/faq.md                            |  6 +--
 examples/azure-pipelines-minimal.yml   |  6 +--
 examples/circleci-minimal.yml          |  6 +--
 examples/cirrus-ci-intel-mac.yml       |  2 +-
 examples/cirrus-ci-minimal.yml         |  2 +-
 examples/github-deploy.yml             |  2 +-
 examples/github-minimal.yml            |  2 +-
 examples/github-pipx.yml               |  2 +-
 examples/github-with-qemu.yml          |  2 +-
 examples/gitlab-minimal.yml            |  6 +--
 examples/gitlab-with-qemu.yml          |  2 +-
 examples/travis-ci-deploy.yml          |  2 +-
 examples/travis-ci-minimal.yml         |  2 +-
 examples/travis-ci-test-and-deploy.yml |  4 +-
 pyproject.toml                         |  2 +-
 18 files changed, 47 insertions(+), 65 deletions(-)

diff --git a/README.md b/README.md
index d82815d73..eecae7988 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ jobs:
       - uses: actions/setup-python@v5
 
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel==3.1.2
+        run: python -m pip install cibuildwheel==3.1.3
 
       - name: Build wheels
         run: python -m cibuildwheel --output-dir wheelhouse
@@ -227,6 +227,16 @@ Changelog
 
 <!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
 
+### v3.1.3
+
+_1 August 2025_
+
+- 🐛 Fix bug where "latest" dependencies couldn't update to pip 25.2 on Windows (#2537)
+- 🛠 Use pytest-rerunfailures to improve some of our iOS/Android tests (#2527, #2539)
+- 🛠 Remove some GraalPy Windows workarounds in our tests (#2501)
+
+
+
 ### v3.1.2
 
 _29 July 2025_
@@ -274,45 +284,7 @@ _5 July 2025_
 - 🛠 Updates CPython 3.14 prerelease to 3.14.0b3 (#2471)
 - ✨ Adds a CPython 3.14 prerelease iOS build (only when prerelease builds are [enabled](https://cibuildwheel.pypa.io/en/stable/options/#enable)) (#2475)
 
-### v3.0.0
-
-_11 June 2025_
-
-See @henryiii's [release post](https://iscinumpy.dev/post/cibuildwheel-3-0-0/) for more info on new features!
-
-- 🌟 Adds the ability to [build wheels for iOS](https://cibuildwheel.pypa.io/en/stable/platforms/#ios)! Set the [`platform` option](https://cibuildwheel.pypa.io/en/stable/options/#platform) to `ios` on a Mac with the iOS toolchain to try it out! (#2286, #2363, #2432)
-- 🌟 Adds support for the GraalPy interpreter! Enable for your project using the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable). (#1538, #2411, #2414)
-- ✨ Adds CPython 3.14 support, under the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) `cpython-prerelease`. This version of cibuildwheel uses 3.14.0b2. (#2390)
-
-    _While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.14 will be available in cibuildwheel without the flag._ (#2390)
-
-- ✨ Adds the [test-sources option](https://cibuildwheel.pypa.io/en/stable/options/#test-sources), which copies files and folders into the temporary working directory we run tests from. (#2062, #2284, #2420, #2437)
-
-    This is particularly important for iOS builds, which do not support placeholders in the `test-command`, but can also be useful for other platforms.
-
-- ✨ Adds [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) inline syntax (#2122)
-- ✨ Improves support for Pyodide builds and adds the experimental [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) option, which allows you to specify the version of Pyodide to use for builds. (#2002)
-- ✨ Add `pyodide-prerelease` [enable](https://cibuildwheel.pypa.io/en/stable/options/#enable) option, with an early build of 0.28 (Python 3.13). (#2431)
-- ✨ Adds the [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) option, which allows you to set environment variables for the test command. (#2388)
-- ✨ Adds the [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) option, which allows you to specify tools safe for cross-compilation. Currently only used on iOS; will be useful for Android in the future. (#2317)
-- 🛠 The default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) has changed from `manylinux2014` to `manylinux_2_28`. (#2330)
-- 🛠 EOL images `manylinux1`, `manylinux2010`, `manylinux_2_24` and `musllinux_1_1` can no longer be specified by their shortname. The full OCI name can still be used for these images, if you wish. (#2316)
-- 🛠 Invokes `build` rather than `pip wheel` to build wheels by default. You can control this via the [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) option. You might notice that you can see your build log output now! (#2321)
-- 🛠 Build verbosity settings have been reworked to have consistent meanings between build backends when non-zero. (#2339)
-- 🛠 Removed the `CIBW_PRERELEASE_PYTHONS` and `CIBW_FREE_THREADED_SUPPORT` options - these have been folded into the [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) option instead. (#2095)
-- 🛠 Build environments no longer have setuptools and wheel preinstalled. (#2329)
-- 🛠 Use the standard Schema line for the integrated JSONSchema. (#2433)
-- ⚠️ Dropped support for building Python 3.6 and 3.7 wheels. If you need to build wheels for these versions, use cibuildwheel v2.23.3 or earlier. (#2282)
-- ⚠️ The minimum Python version required to run cibuildwheel is now Python 3.11. You can still build wheels for Python 3.8 and newer. (#1912)
-- ⚠️ 32-bit Linux wheels no longer built by default - the [arch](https://cibuildwheel.pypa.io/en/stable/options/#archs) was removed from `"auto"`. It now requires explicit `"auto32"`. Note that modern manylinux images (like the new default, `manylinux_2_28`) do not have 32-bit versions. (#2458)
-- ⚠️ PyPy wheels no longer built by default, due to a change to our options system. To continue building PyPy wheels, you'll now need to set the [`enable` option](https://cibuildwheel.pypa.io/en/stable/options/#enable) to `pypy` or `pypy-eol`. (#2095)
-- ⚠️ Dropped official support for Appveyor. If it was working for you before, it will probably continue to do so, but we can't be sure, because our CI doesn't run there anymore. (#2386)
-- 📚 A reorganisation of the docs, and numerous updates. (#2280)
-- 📚 Use Python 3.14 color output in docs CLI output. (#2407)
-- 📚 Docs now primarily use the pyproject.toml name of options, rather than the environment variable name. (#2389)
-- 📚 README table now matches docs and auto-updates. (#2427, #2428)
-
-<!-- [[[end]]] (sum: sVC5DNuhaF) -->
+<!-- [[[end]]] (sum: Fe+mD8YR4+) -->
 
 ---
 
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 911557b86..f7493720d 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1 +1 @@
-__version__ = "3.1.2"
+__version__ = "3.1.3"
diff --git a/docs/changelog.md b/docs/changelog.md
index a0223405b..5a4f223f1 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,16 @@ title: Changelog
 
 # Changelog
 
+### v3.1.3
+
+_1 August 2025_
+
+- 🐛 Fix bug where "latest" dependencies couldn't update to pip 25.2 on Windows (#2537)
+- 🛠 Use pytest-rerunfailures to improve some of our iOS/Android tests (#2527, #2539)
+- 🛠 Remove some GraalPy Windows workarounds in our tests (#2501)
+
+
+
 ### v3.1.2
 
 _29 July 2025_
diff --git a/docs/faq.md b/docs/faq.md
index 6cbc78780..32e83515d 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -110,7 +110,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
 If you use GitHub Actions for builds, you can use cibuildwheel as an action:
 
 ```yaml
-uses: pypa/cibuildwheel@v3.1.2
+uses: pypa/cibuildwheel@v3.1.3
 ```
 
 This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -132,7 +132,7 @@ The second option, and the only one that supports other CI systems, is using a `
 
 ```bash
 # requirements-cibw.txt
-cibuildwheel==3.1.2
+cibuildwheel==3.1.3
 ```
 
 Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -297,7 +297,7 @@ Solutions to this vary, but the simplest is to use pipx:
 # most runners have pipx preinstalled, but in case you don't
 python3 -m pip install pipx
 
-pipx run cibuildwheel==3.1.2 --output-dir wheelhouse
+pipx run cibuildwheel==3.1.3 --output-dir wheelhouse
 pipx run twine upload wheelhouse/*.whl
 ```
 
diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml
index df8765af1..ba4549f93 100644
--- a/examples/azure-pipelines-minimal.yml
+++ b/examples/azure-pipelines-minimal.yml
@@ -6,7 +6,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        pip3 install cibuildwheel==3.1.2
+        pip3 install cibuildwheel==3.1.3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
     - bash: |
         set -o errexit
         python3 -m pip install --upgrade pip
-        python3 -m pip install cibuildwheel==3.1.2
+        python3 -m pip install cibuildwheel==3.1.3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
     - bash: |
         set -o errexit
         python -m pip install --upgrade pip
-        pip install cibuildwheel==3.1.2
+        pip install cibuildwheel==3.1.3
       displayName: Install dependencies
     - bash: cibuildwheel --output-dir wheelhouse .
       displayName: Build wheels
diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml
index 6e548d6c4..2a41d94e9 100644
--- a/examples/circleci-minimal.yml
+++ b/examples/circleci-minimal.yml
@@ -11,7 +11,7 @@ jobs:
       - run:
           name: Build the Linux wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.1.2
+            python3 -m pip install --user cibuildwheel==3.1.3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
       - run:
           name: Build the Linux aarch64 wheels.
           command: |
-            python3 -m pip install --user cibuildwheel==3.1.2
+            python3 -m pip install --user cibuildwheel==3.1.3
             python3 -m cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
           name: Build the OS X wheels.
           command: |
             sudo softwareupdate --install-rosetta --agree-to-license  # for python<=3.8 or x86_64/universal2 tests
-            pip3 install cibuildwheel==3.1.2
+            pip3 install cibuildwheel==3.1.3
             cibuildwheel --output-dir wheelhouse
       - store_artifacts:
           path: wheelhouse/
diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml
index 321dc419a..8c0a05aff 100644
--- a/examples/cirrus-ci-intel-mac.yml
+++ b/examples/cirrus-ci-intel-mac.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.1.2
+    - python -m pip install cibuildwheel==3.1.3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml
index d18547d4d..6de8cb455 100644
--- a/examples/cirrus-ci-minimal.yml
+++ b/examples/cirrus-ci-minimal.yml
@@ -1,6 +1,6 @@
 build_and_store_wheels: &BUILD_AND_STORE_WHEELS
   install_cibuildwheel_script:
-    - python -m pip install cibuildwheel==3.1.2
+    - python -m pip install cibuildwheel==3.1.3
   run_cibuildwheel_script:
     - cibuildwheel
   wheels_artifacts:
diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml
index fdd7ae892..f4823c033 100644
--- a/examples/github-deploy.yml
+++ b/examples/github-deploy.yml
@@ -61,7 +61,7 @@ jobs:
           sudo udevadm trigger --name-match=kvm
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.2
+        uses: pypa/cibuildwheel@v3.1.3
         env:
           CIBW_PLATFORM: ${{ matrix.platform || 'auto' }}
         # Can also be configured directly, using `with:`
diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml
index d2f68b390..03408eb6e 100644
--- a/examples/github-minimal.yml
+++ b/examples/github-minimal.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.2
+        uses: pypa/cibuildwheel@v3.1.3
         # env:
         #   CIBW_SOME_OPTION: value
         #   ...
diff --git a/examples/github-pipx.yml b/examples/github-pipx.yml
index 2fa452582..dbf5f9fff 100644
--- a/examples/github-pipx.yml
+++ b/examples/github-pipx.yml
@@ -15,7 +15,7 @@ jobs:
       - uses: actions/checkout@v4
 
       - name: Build wheels
-        run: pipx run cibuildwheel==3.1.2
+        run: pipx run cibuildwheel==3.1.3
 
       - uses: actions/upload-artifact@v4
         with:
diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml
index 39211a273..6d489647c 100644
--- a/examples/github-with-qemu.yml
+++ b/examples/github-with-qemu.yml
@@ -21,7 +21,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v3.1.2
+        uses: pypa/cibuildwheel@v3.1.3
         env:
           # configure cibuildwheel on Linux to build native archs ('auto'),
           # and to split the remaining architectures between the x86_64 and
diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml
index 314e1bf96..91a480848 100644
--- a/examples/gitlab-minimal.yml
+++ b/examples/gitlab-minimal.yml
@@ -12,7 +12,7 @@ linux:
     DOCKER_TLS_CERTDIR: ""
   script:
     - curl -sSL https://get.docker.com/ | sh
-    - python -m pip install cibuildwheel==3.1.2
+    - python -m pip install cibuildwheel==3.1.3
     - cibuildwheel --output-dir wheelhouse
   artifacts:
     paths:
@@ -23,7 +23,7 @@ windows:
   before_script:
     - choco install python -y --allow-downgrade --version 3.12.4
     - choco install git.install -y
-    - py -m pip install cibuildwheel==3.1.2
+    - py -m pip install cibuildwheel==3.1.3
   script:
     - py -m cibuildwheel --output-dir wheelhouse --platform windows
   artifacts:
@@ -35,7 +35,7 @@ windows:
 macos:
   image: macos-14-xcode-15
   before_script:
-    - python3 -m pip install cibuildwheel==3.1.2
+    - python3 -m pip install cibuildwheel==3.1.3
   script:
     - python3 -m cibuildwheel --output-dir wheelhouse
   artifacts:
diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml
index c8b175c46..759d201f9 100644
--- a/examples/gitlab-with-qemu.yml
+++ b/examples/gitlab-with-qemu.yml
@@ -14,7 +14,7 @@ linux:
     - curl -sSL https://get.docker.com/ | sh
     # Warning: This is extremely slow, be careful with how many wheels you build
     - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
-    - python -m pip install cibuildwheel==3.1.2
+    - python -m pip install cibuildwheel==3.1.3
     # Assuming your CI runner's default architecture is x86_64...
     - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
   artifacts:
diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml
index d59351707..65043d88b 100644
--- a/examples/travis-ci-deploy.yml
+++ b/examples/travis-ci-deploy.yml
@@ -20,7 +20,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.1.2
+  - python3 -m pip install cibuildwheel==3.1.3
 
 script:
   # build the wheels, put them into './dist'
diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml
index a7f1ac4bc..c71a8f5eb 100644
--- a/examples/travis-ci-minimal.yml
+++ b/examples/travis-ci-minimal.yml
@@ -26,7 +26,7 @@ jobs:
         - ln -s /c/Python312/python.exe /c/Python312/python3.exe
 
 install:
-  - python3 -m pip install cibuildwheel==3.1.2
+  - python3 -m pip install cibuildwheel==3.1.3
 
 script:
   # build the wheels, put them into './wheelhouse'
diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml
index e458565c5..29d9fddfb 100644
--- a/examples/travis-ci-test-and-deploy.yml
+++ b/examples/travis-ci-test-and-deploy.yml
@@ -52,7 +52,7 @@ jobs:
     - stage: deploy
       name: Build and deploy Linux wheels
       services: docker
-      install: python3 -m pip install cibuildwheel==3.1.2 twine
+      install: python3 -m pip install cibuildwheel==3.1.3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
     # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
       name: Build and deploy Windows wheels
       os: windows
       language: shell
-      install: python3 -m pip install cibuildwheel==3.1.2 twine
+      install: python3 -m pip install cibuildwheel==3.1.3 twine
       script: python3 -m cibuildwheel --output-dir wheelhouse
       after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
 
diff --git a/pyproject.toml b/pyproject.toml
index f1cd7328b..d1bfd6e8b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "cibuildwheel"
-version = "3.1.2"
+version = "3.1.3"
 description = "Build Python wheels on CI with minimal configuration."
 readme = "README.md"
 license = "BSD-2-Clause"
