Use in CI/CD pipelines#

conda-exec lets CI jobs run tools from conda packages without permanent installation, keeping the CI environment clean.

Install conda-exec#

Install conda-exec and its solver dependency into the base environment:

conda install -n base conda-exec conda-rattler-solver

The standalone ce command is also available after installation:

ce ruff check .

Run tools without permanent installation#

Instead of adding linters, formatters, or other tools to your project’s environment, run them ephemerally:

conda exec ruff check .
conda exec black --check src/
conda exec mypy src/

Each tool gets its own isolated environment. Nothing is installed into your project’s conda environment.

Non-interactive mode#

The --clean subcommand prompts for confirmation by default. In CI, pass -y or --yes to skip the prompt:

conda exec --clean --all --yes

Other subcommands (conda exec TOOL, --list, --refresh) do not prompt and work in CI without extra flags.

Cache persistence#

Tip

Caching the conda-exec environment directory between CI runs can cut tool startup time from seconds to near zero. The first run pays the cost of solving and downloading; every run after that reuses the cached result.

conda-exec stores cached environments in ~/.conda/exec/envs/. To speed up CI runs, cache this directory between jobs.

- uses: actions/cache@v4
  with:
    path: ~/.conda/exec/envs
    key: conda-exec-${{ runner.os }}

Cache the directory ~/.conda/exec/envs using your CI provider’s cache mechanism. The key should include the OS to avoid cross-platform conflicts.

If your CI supports a save/restore cache step:

# restore
cp -a /cache/conda-exec-envs ~/.conda/exec/envs 2>/dev/null || true

# ... run your jobs ...

# save
cp -a ~/.conda/exec/envs /cache/conda-exec-envs

With caching enabled, only the first CI run resolves and downloads packages. Subsequent runs reuse the cached environments and start instantly.

Example: GitHub Actions workflow#

A complete workflow that uses conda-exec for linting:

name: Lint
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: conda-incubator/setup-miniconda@v3
        with:
          activate-environment: ""
          auto-activate-base: true

      - name: Install conda-exec
        run: conda install -n base -y conda-exec conda-rattler-solver

      - uses: actions/cache@v4
        with:
          path: ~/.conda/exec/envs
          key: conda-exec-${{ runner.os }}

      - name: Lint
        run: conda exec ruff check .

      - name: Format check
        run: conda exec black --check src/

Run scripts with inline dependencies#

CI jobs can run Python scripts that declare their own dependencies, without any prior setup beyond conda-exec itself:

conda exec analysis.py --output results.json

The script’s PEP 723 metadata block tells conda-exec exactly which packages to install.

Clean up after CI#

To free disk space at the end of a CI job (useful on self-hosted runners):

conda exec --clean --all --yes

Override the cache directory#

Set CONDA_EXEC_HOME to control where cached environments are stored:

export CONDA_EXEC_HOME=/tmp/conda-exec-cache
conda exec ruff check .

This is useful when the CI runner’s home directory is not suitable for caching.