diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml
index 9de63b14c4fd..a145b5bbcc08 100644
--- a/.github/workflows/cibuildwheel.yml
+++ b/.github/workflows/cibuildwheel.yml
@@ -124,7 +124,7 @@ jobs:
             cibw_archs: "aarch64"
           - os: windows-latest
             cibw_archs: "auto64"
-          - os: macos-12
+          - os: macos-13
             cibw_archs: "x86_64"
           - os: macos-14
             cibw_archs: "arm64"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index cd4c08e29fb9..4100fda43a36 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -101,7 +101,7 @@ jobs:
             pyqt6-ver: '!=6.6.0'
             # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
             pyside6-ver: '!=6.5.1'
-          - os: macos-12  # This runner is on Intel chips.
+          - os: macos-13  # This runner is on Intel chips.
             python-version: '3.9'
             # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
             pyside6-ver: '!=6.5.1'
@@ -192,6 +192,17 @@ jobs:
             ;;
           macOS)
             brew update
+            # Periodically, Homebrew updates Python and fails to overwrite the
+            # existing not-managed-by-Homebrew copy without explicitly being told
+            # to do so. GitHub/Azure continues to avoid fixing their runner images:
+            # https://github.com/actions/runner-images/issues/9966
+            # so force an overwrite even if there are no Python updates.
+            # We don't even care about Homebrew's Python because we use the one
+            # from actions/setup-python.
+            for python_package in $(brew list | grep python@); do
+              brew unlink ${python_package}
+              brew link --overwrite ${python_package}
+            done
             brew install ccache ghostscript gobject-introspection gtk4 ninja
             brew install --cask font-noto-sans-cjk inkscape
             ;;
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 4c50c543846a..e2829ce81e9f 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -120,6 +120,17 @@ stages:
                 ;;
               Darwin)
                 brew update
+                # Periodically, Homebrew updates Python and fails to overwrite the
+                # existing not-managed-by-Homebrew copy without explicitly being told
+                # to do so. GitHub/Azure continues to avoid fixing their runner images:
+                # https://github.com/actions/runner-images/issues/9966
+                # so force an overwrite even if there are no Python updates.
+                # We don't even care about Homebrew's Python because we use the one
+                # from UsePythonVersion.
+                for python_package in $(brew list | grep python@); do
+                  brew unlink ${python_package}
+                  brew link --overwrite ${python_package}
+                done
                 brew install --cask xquartz
                 brew install ccache ffmpeg imagemagick mplayer ninja pkg-config
                 brew install --cask font-noto-sans-cjk-sc