Repository: pre-commit/pre-commit

Issue:
Allow to `mamba install` environments
Thanks for maintaining pre-commit, we really enjoying working with it!

When installing conda environments, [mamba](https://github.com/mamba-org/mamba) is often the preferred option, as it is faster and [requires less memory](https://github.com/conda/conda/issues/5003) when solving an environment.

As of today, pre-commit uses conda for creating environments, as this is hard-coded into the source code:

https://github.com/pre-commit/pre-commit/blob/b944395d6638629a93756ed7279b4368473177af/pre_commit/languages/conda.py#L62-L66

We have a use case, where we want to install pre-commit hooks in CI, but the installation fails because of out-of-memory errors. Installing the same environment with mamba however, succeeds.

It would be nice if one could define whether pre-commit uses conda or mamba for installing environments. This could for example be accomplished by introducing an environment variable `PRE_COMMIT_USE_MAMBA`. If there is a better way to pass such configuration to pre-commit, please let me know.

I'd be glad to work on this feature, if this is something you'd like to see added to pre-commit.


Discussion:
if it's drop in presumably you could `ln -s $(which mamba) ~/bin/conda` or something of the sort?
Thanks @janjagusch for opening this issue. I also thought already about opening one here. There are some open points why I didn't yet make one here:

- `mamba` is a 90% drop-in for the end user, you still want to disable it in some rare but regular situations.
- You cannot do `ln -s $(which mamba) ~/bin/conda` as this will also break a lot of other things, e.g. conda's shell activation which is slightly different from `mamba`'s. This might converge in the near future but has not and might never do.
- You cannot only select nowadays between `mamba` and `conda` but there is also `micromamba` available. This is a statically linked binary compatible with the `install` and `env` commands of `mamba`/`conda` but differing on some other aspects. If `mamba` is supported, one should also add support for `micromamba`.

Normally, I have implemented this via the pseudocode in other tools:

```
conda_exe = shutil.which("mamba")
if not conda_exe or os.environ.get("PRE_COMMIT_USE_MAMBA", "1") == "0":
  mamba_exe = "conda"
```

An alternative in pre-commit could also be to implement it as another language, e.g. if you want to use `mamba`, use `language: mamba`. Additionally, this also could open up the possibility to use `micromamba` as a third alternative without the need for another environment variable.

Resolving patch:
diff --git a/pre_commit/languages/conda.py b/pre_commit/languages/conda.py
--- a/pre_commit/languages/conda.py
+++ b/pre_commit/languages/conda.py
@@ -50,6 +50,15 @@ def in_env(
         yield
 
 
+def _conda_exe() -> str:
+    if os.environ.get('PRE_COMMIT_USE_MICROMAMBA'):
+        return 'micromamba'
+    elif os.environ.get('PRE_COMMIT_USE_MAMBA'):
+        return 'mamba'
+    else:
+        return 'conda'
+
+
 def install_environment(
         prefix: Prefix,
         version: str,
@@ -58,15 +67,17 @@ def install_environment(
     helpers.assert_version_default('conda', version)
     directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
 
+    conda_exe = _conda_exe()
+
     env_dir = prefix.path(directory)
     with clean_path_on_failure(env_dir):
         cmd_output_b(
-            'conda', 'env', 'create', '-p', env_dir, '--file',
+            conda_exe, 'env', 'create', '-p', env_dir, '--file',
             'environment.yml', cwd=prefix.prefix_dir,
         )
         if additional_dependencies:
             cmd_output_b(
-                'conda', 'install', '-p', env_dir, *additional_dependencies,
+                conda_exe, 'install', '-p', env_dir, *additional_dependencies,
                 cwd=prefix.prefix_dir,
             )
 


Test patch:
diff --git a/tests/languages/conda_test.py b/tests/languages/conda_test.py
new file mode 100644
--- /dev/null
+++ b/tests/languages/conda_test.py
@@ -0,0 +1,38 @@
+import pytest
+
+from pre_commit import envcontext
+from pre_commit.languages.conda import _conda_exe
+
+
+@pytest.mark.parametrize(
+    ('ctx', 'expected'),
+    (
+        pytest.param(
+            (
+                ('PRE_COMMIT_USE_MICROMAMBA', envcontext.UNSET),
+                ('PRE_COMMIT_USE_MAMBA', envcontext.UNSET),
+            ),
+            'conda',
+            id='default',
+        ),
+        pytest.param(
+            (
+                ('PRE_COMMIT_USE_MICROMAMBA', '1'),
+                ('PRE_COMMIT_USE_MAMBA', ''),
+            ),
+            'micromamba',
+            id='default',
+        ),
+        pytest.param(
+            (
+                ('PRE_COMMIT_USE_MICROMAMBA', ''),
+                ('PRE_COMMIT_USE_MAMBA', '1'),
+            ),
+            'mamba',
+            id='default',
+        ),
+    ),
+)
+def test_conda_exe(ctx, expected):
+    with envcontext.envcontext(ctx):
+        assert _conda_exe() == expected