diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml
index 96e581179..08e98cdbf 100644
--- a/.github/workflows/build_wheels.yml
+++ b/.github/workflows/build_wheels.yml
@@ -50,12 +50,12 @@ jobs:
         # Github Actions doesn't support pairing matrix values together, let's improvise
         # https://github.com/github/feedback/discussions/7835#discussioncomment-1769026
         buildplat:
-          - [ubuntu-20.04, manylinux_x86_64]
-          - [ubuntu-20.04, musllinux_x86_64]
-          - [macos-12, macosx_x86_64]
-          - [macos-12, macosx_arm64]
-          - [windows-2019, win_amd64]
-        python-version: ['3.8', '3.9', '3.10', '3.11', 'pypy3.8', 'pypy3.9']
+          - [ubuntu-22.04, manylinux_x86_64]
+          - [ubuntu-22.04, musllinux_x86_64]
+          - [macos-13, macosx_x86_64]
+          - [macos-13, macosx_arm64]
+          - [windows-2022, win_amd64]
+        python-version: ['3.9', '3.10', '3.12', 'pypy3.10']
 
     steps:
       - uses: actions/checkout@v4
@@ -65,9 +65,10 @@ jobs:
         with:
           python-version: ${{ matrix.python-version }}
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.16.5
-      - uses: actions/upload-artifact@v3
+        uses: pypa/cibuildwheel@v2.21.3
+      - uses: actions/upload-artifact@v4
         with:
+          name: cibw-wheels-cp${{ matrix.python-version }}-${{ matrix.buildplat[1] }}
           path: ./wheelhouse/*.whl
 
   build_sdist:
@@ -86,6 +87,6 @@ jobs:
         shell: bash -l {0}
         run: pipx run build --sdist
 
-      - uses: actions/upload-artifact@v3
+      - uses: actions/upload-artifact@v4
         with:
           path: dist/*.tar.gz
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c8ca29766..949bc38b7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,8 +13,9 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: ["ubuntu-latest"]
-        python-version: ["3.7", "3.12", "pypy-3.9"]
+        # macos-latest doesn't have 3.8 anymore
+        os: ["ubuntu-latest", "macos-13"]
+        python-version: ["3.8", "3.12", "pypy-3.10"]
         r-version: ['release']
     timeout-minutes: 60
     steps:
@@ -22,15 +23,22 @@ jobs:
         with:
           fetch-depth: 0
 
-      # We need Python 3.7 to always be installed, so tests with
+      # We need Python 3.8 to always be installed, so tests with
       # multiple environments can run.
-      - name: Set up Python 3.7
-        uses: actions/setup-python@v4
+      - name: Set up Python 3.8
+        uses: actions/setup-python@v5
         with:
-          python-version: 3.7
+          python-version: 3.8
+
+      - name: Install mercurial and conda
+        if: runner.os == 'macOS'
+        run: |
+          brew update
+          brew install mercurial
+          brew install --cask anaconda
 
       - name: Set up Python version ${{ matrix.python-version }}
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
           cache: pip
@@ -40,17 +48,17 @@ jobs:
         uses: browser-actions/setup-chrome@latest
 
       - name: Set up R version ${{ matrix.r-version }}
-        uses: r-lib/actions/setup-r@v2
+        uses: r-lib/actions/setup-r@v2.11.0
         with:
           r-version: ${{ matrix.r-version }}
 
       - name: Install dependencies (standard)
         if: matrix.python-version != '3.12.0-rc.2'
-        run: python -m pip install ".[test,hg]"
+        run: python -m pip install ".[test,hg,testR]"
 
       - name: Install dependencies (with --pre)
         if: matrix.python-version == '3.12.0-rc.2'
-        run: python -m pip install ".[test,hg]" --pre
+        run: python -m pip install ".[test,hg,testR]" --pre
 
       - name: Install asv
         run: pip install .
@@ -72,12 +80,7 @@ jobs:
         with:
           fetch-depth: 0
 
-      - name: Set up R version ${{ matrix.r-version }}
-        uses: r-lib/actions/setup-r@v2
-        with:
-          r-version: ${{ matrix.r-version }}
-
-      - uses: mamba-org/setup-micromamba@v1
+      - uses: mamba-org/setup-micromamba@v2.0.1
         with:
           init-shell: >-
             bash
@@ -86,7 +89,7 @@ jobs:
           create-args: >-
             python
             pip
-            libmambapy
+            libmambapy<2.0
             conda-build
 
       - name: Install dependencies
@@ -106,7 +109,7 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
-      - uses: actions/setup-python@v4
+      - uses: actions/setup-python@v5
         with:
           python-version: '3.x'
           cache: pip
diff --git a/.github/workflows/ci_win.yml b/.github/workflows/ci_win.yml
index 3e2038a6c..c337fb5f1 100644
--- a/.github/workflows/ci_win.yml
+++ b/.github/workflows/ci_win.yml
@@ -14,7 +14,7 @@ jobs:
       fail-fast: false
       matrix:
         os: ["windows-latest"]
-        python-version: ["3.7"]
+        python-version: ["3.8"]
     timeout-minutes: 60
     steps:
       - uses: actions/checkout@v4
@@ -22,7 +22,7 @@ jobs:
           fetch-depth: 0
 
       - name: Set up Python version ${{ matrix.python-version }}
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
           cache: pip
@@ -38,7 +38,7 @@ jobs:
 
   test_env:
     name: test_environments
-    runs-on: "ubuntu-latest"
+    runs-on: "windows-latest"
     strategy:
       fail-fast: false
     timeout-minutes: 10
@@ -47,26 +47,25 @@ jobs:
         with:
           fetch-depth: 0
 
-      - uses: mamba-org/setup-micromamba@v1
+      - uses: mamba-org/setup-micromamba@v2.0.1
         with:
-          init-shell: >-
-            powershell
+          init-shell: powershell
           environment-name: test-env
           cache-environment: true
           create-args: >-
             python
             pip
-            libmambapy
+            libmambapy<2.0
             conda-build
 
       - name: Install dependencies
         run: python -m pip install ".[test,hg]" --pre
-        shell: micromamba-shell {0}
+        shell: pwsh
 
       - name: Install asv
         run: pip install .
-        shell: micromamba-shell {0}
+        shell: pwsh
 
       - name: Run tests
-        run: pytest -k environment_bench -vvvvv
-        shell: micromamba-shell {0}
+        run: python -m pytest -k environment_bench -vvvvv
+        shell: pwsh
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index 2bf9a57d8..25570be86 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v4
-    - uses: actions/setup-python@v4
+    - uses: actions/setup-python@v5
       with:
-        python-version: '3.9'
-    - uses: pre-commit/action@v2.0.3
+        python-version: '3.12'
+    - uses: pre-commit/action@v3.0.1
diff --git a/.github/workflows/triggered.yml b/.github/workflows/triggered.yml
index 94f08ce1e..c03707d27 100644
--- a/.github/workflows/triggered.yml
+++ b/.github/workflows/triggered.yml
@@ -19,7 +19,7 @@ jobs:
       fail-fast: false
       matrix:
         os: ["ubuntu-latest"]
-        python-version: ["3.7", "3.10", "pypy-3.8", "pypy-3.9"]
+        python-version: ["3.8", "3.12", "pypy-3.10"]
         r-version: ['release']
     timeout-minutes: 30
     steps:
@@ -27,15 +27,15 @@ jobs:
         with:
           fetch-depth: 0
 
-      # We need Python 3.7 to always be installed, so tests with
+      # We need Python 3.8 to always be installed, so tests with
       # multiple environments can run.
-      - name: Set up Python 3.7
-        uses: actions/setup-python@v4
+      - name: Set up Python 3.8
+        uses: actions/setup-python@v5
         with:
-          python-version: 3.7
+          python-version: 3.8
 
       - name: Set up Python version ${{ matrix.python-version }}
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
           cache: pip
@@ -45,7 +45,7 @@ jobs:
         uses: browser-actions/setup-chrome@latest
 
       - name: Set up R version ${{ matrix.r-version }}
-        uses: r-lib/actions/setup-r@v2
+        uses: r-lib/actions/setup-r@v2.11.0
         with:
           r-version: ${{ matrix.r-version }}
 
diff --git a/asv/plugins/_mamba_helpers.py b/asv/plugins/_mamba_helpers.py
index 3b15d6bf9..0aec5d936 100644
--- a/asv/plugins/_mamba_helpers.py
+++ b/asv/plugins/_mamba_helpers.py
@@ -12,6 +12,8 @@
 from conda.core.index import check_allowlist
 from conda.gateways.connection.session import CondaHttpAuth
 
+from ..console import log
+
 
 def get_index(
     channel_urls=(),
@@ -145,12 +147,12 @@ def load_channels(
             continue
 
         if context.verbosity != 0 and not context.json:
-            print(
+            log.info(
                 "Channel: {}, platform: {}, prio: {} : {}".format(
                     entry["channel"], entry["platform"], priority, subpriority
                 )
             )
-            print("Cache path: ", subdir.cache_path())
+            log.info("Cache path: ", subdir.cache_path())
 
         repo = subdir.create_repo(pool)
         repo.set_priority(priority, subpriority)
@@ -254,8 +256,7 @@ def solve(self, specs, pkg_cache_path=None):
             for c in self.channels:
                 error_string += f" - {c}\n"
             error_string += api_solver.explain_problems()
-            print(error_string)
-            raise RuntimeError("Solver could not find solution." + error_string)
+            raise RuntimeError(f"Solver could not find solution, got:\n{error_string}")
 
         if pkg_cache_path is None:
             # use values from conda
diff --git a/asv/plugins/mamba.py b/asv/plugins/mamba.py
index 8e0be1b7b..731fd07f3 100644
--- a/asv/plugins/mamba.py
+++ b/asv/plugins/mamba.py
@@ -13,12 +13,18 @@
     from yaml import Loader
 
 import libmambapy
+from importlib_metadata import version as get_version
 
-from ._mamba_helpers import MambaSolver
 from .. import environment, util
 from ..console import log
 
-WIN = os.name == "nt"
+if int(get_version('libmambapy').split(".")[0]) >= 2:
+    raise environment.EnvironmentUnavailable(
+        f"libmambapy must be less than 2.0, but got {get_version('libmambapy')}"
+    )
+
+
+from ._mamba_helpers import MambaSolver
 
 # Like Conda, Mamba also needs to be serialized
 util.new_multiprocessing_lock("mamba_lock")
diff --git a/pyproject.toml b/pyproject.toml
index 89ca606c1..0bda0c2c3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -61,7 +61,6 @@ test = [
     "flaky",
     "pytest-rerunfailures",
     "python-hglib; platform_system != 'Windows'",
-    "rpy2; platform_system != 'Windows' and platform_python_implementation != 'PyPy'",
 ]
 doc = [
     "sphinx",
@@ -82,6 +81,9 @@ hg = [
 plugs = [
     "asv-bench-memray",
 ]
+testR = [
+    "rpy2; platform_system != 'Windows' and platform_python_implementation != 'PyPy'",
+]
 all = ["asv[doc,dev,hg,plugs]"]
 [build-system]
 requires = [
diff --git a/test/test_environment.py b/test/test_environment.py
index 6915811ad..5f66db878 100644
--- a/test/test_environment.py
+++ b/test/test_environment.py
@@ -617,7 +617,7 @@ def test_environment_name_sanitization():
                  marks=pytest.mark.skipif(not HAS_CONDA, reason="needs conda and conda-build")),
     pytest.param("virtualenv",
                  marks=pytest.mark.skipif(not (HAS_PYTHON_VER2 and HAS_VIRTUALENV),
-                                          reason="needs virtualenv and python 3.7"))
+                                          reason="needs virtualenv and python 3.8"))
 ])
 def test_environment_environ_path(environment_type, tmpdir, monkeypatch):
     # Check that virtualenv binary dirs are in the PATH
diff --git a/test/tools.py b/test/tools.py
index dd6eb2eb3..6d2480edf 100644
--- a/test/tools.py
+++ b/test/tools.py
@@ -36,11 +36,11 @@
 from asv.plugins.conda import _find_conda
 
 # Two Python versions for testing
-PYTHON_VER1, PYTHON_VER2 = '3.7', platform.python_version()
+PYTHON_VER1, PYTHON_VER2 = '3.8', platform.python_version()
 
 # Installable library versions to use in tests
 DUMMY1_VERSION = "0.14"
-DUMMY2_VERSIONS = ["0.3.7", "0.3.9"]
+DUMMY2_VERSIONS = ["0.3.8", "0.3.9"]
 
 
 WIN = (os.name == "nt")