diff --git a/README.md b/README.md index ad94178e..9cb112b7 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,10 @@ conda install -c conda-forge conda-lock # generate a multi-platform lockfile conda-lock -f environment.yml -p osx-64 -p linux-64 +# optionally, update the previous solution, using the latest version of +# pydantic that is compatible with the source specification +conda-lock --update pydantic + # create an environment from the lockfile conda-lock install [-p {prefix}|-n {name}] @@ -41,11 +45,21 @@ conda create -n my-locked-env --file conda-linux-64.lock ### File naming -By default conda-lock will name rendered files as `"conda-{platform}.lock"`. +By default, `conda-lock` store its output in `conda-lock.yml` in the current +working directory. This file will also be used by default for render, install, +and update operations. You can supply a different filename with e.g. + +```bash +conda-lock --lockfile superspecial.conda-lock.yml +``` + +The extension `.conda-lock.yml` will be added if not present. Rendered +environment files (env or explicit) will be named as as +`"conda-{platform}.lock"`. If you want to override that call conda-lock as follows. ```bash -conda-lock --filename-template "specific-{platform}.conda.lock" +conda-lock -k explicit --filename-template "specific-{platform}.conda.lock" ``` ### Compound specification diff --git a/conda_lock/conda_lock.py b/conda_lock/conda_lock.py index 28a01624..093b9cb0 100644 --- a/conda_lock/conda_lock.py +++ b/conda_lock/conda_lock.py @@ -834,9 +834,30 @@ def run_lock( update: Optional[List[str]] = None, ) -> None: if environment_files == DEFAULT_FILES: - long_ext_file = pathlib.Path("environment.yaml") - if long_ext_file.exists() and not environment_files[0].exists(): - environment_files = [long_ext_file] + if lockfile_path.exists(): + lock_content = parse_conda_lock_file(lockfile_path) + # reconstruct native paths + locked_environment_files = [ + pathlib.Path( + pathlib.PurePosixPath(lockfile_path).parent + / pathlib.PurePosixPath(p) + ) + for p in lock_content.metadata.sources + ] + if all(p.exists() for p in locked_environment_files): + environment_files = locked_environment_files + else: + missing = [p for p in locked_environment_files if not p.exists()] + print( + f"{lockfile_path} was created from {[str(p) for p in locked_environment_files]}," + f" but some files ({[str(p) for p in missing]}) do not exist. Falling back to" + f" {[str(p) for p in environment_files]}.", + file=sys.stderr, + ) + else: + long_ext_file = pathlib.Path("environment.yaml") + if long_ext_file.exists() and not environment_files[0].exists(): + environment_files = [long_ext_file] _conda_exe = determine_conda_executable( conda_exe, mamba=mamba, micromamba=micromamba diff --git a/tests/test_conda_lock.py b/tests/test_conda_lock.py index 6751bfa8..f57dd63c 100644 --- a/tests/test_conda_lock.py +++ b/tests/test_conda_lock.py @@ -10,11 +10,13 @@ from glob import glob from typing import Any +from unittest.mock import MagicMock from urllib.parse import urldefrag, urlsplit import pytest from conda_lock.conda_lock import ( + DEFAULT_FILES, DEFAULT_LOCKFILE_NAME, _add_auth_to_line, _add_auth_to_lockfile, @@ -354,6 +356,19 @@ def test_run_lock_with_update(monkeypatch, update_environment, conda_exe): assert post_lock["python"].version == pre_lock["python"].version +def test_run_lock_with_locked_environment_files( + monkeypatch, update_environment, conda_exe +): + """run_lock() with default args uses source files from lock""" + monkeypatch.chdir(update_environment.parent) + make_lock_files = MagicMock() + monkeypatch.setattr("conda_lock.conda_lock.make_lock_files", make_lock_files) + run_lock(DEFAULT_FILES, conda_exe=conda_exe, update=["pydantic"]) + assert [p.resolve() for p in make_lock_files.call_args.kwargs["src_files"]] == [ + pathlib.Path(update_environment.parent / "environment-preupdate.yml") + ] + + def test_run_lock_with_pip(monkeypatch, pip_environment, conda_exe): monkeypatch.chdir(pip_environment.parent) if is_micromamba(conda_exe):