diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f25414..3a85684e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed +- `get_cli_string` now quotes the `.env` path so spaces and quotes in directory names do not break the generated POSIX shell command. Shell variables and wildcards in the path are now treated literally. - Fix a package build deprecation warning caused by a non-string `license` value in `pyproject.toml` by [@kurtmckee] in [#648] - `set_key`, `unset_key` and the `dotenv set`/`unset` commands now name the `.env` path instead of an internal temporary file when its directory is missing or not writable, and the CLI prints a short error and exits with code 2 instead of a traceback by [@jamalkamaladdin] in [#711] - `set_key` and `unset_key` no longer leave a `.tmp_*` file behind on Windows when writing a read-only `.env` fails, and the error raised is the one from the failed write rather than from cleaning up the temporary file by [@MohammedAlkindi] in [#686] diff --git a/src/dotenv/__init__.py b/src/dotenv/__init__.py index dde24a01..85ae0636 100644 --- a/src/dotenv/__init__.py +++ b/src/dotenv/__init__.py @@ -1,3 +1,4 @@ +import shlex from typing import Any, Optional from .main import dotenv_values, find_dotenv, get_key, load_dotenv, set_key, unset_key @@ -20,12 +21,15 @@ def get_cli_string( Useful for converting a arguments passed to a fabric task to be passed to a `local` or `run` command. + + The path is quoted for use in a POSIX shell. + Shell variables and wildcards in the path are treated literally. """ command = ["dotenv"] if quote: command.append(f"-q {quote}") if path: - command.append(f"-f {path}") + command.append(f"-f {shlex.quote(str(path))}") if action: command.append(action) if key: diff --git a/tests/test_utils.py b/tests/test_utils.py index 93b8bae2..dd551466 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,10 @@ +import shlex +from pathlib import Path + +import pytest + from dotenv import get_cli_string as c +from dotenv.cli import cli as dotenv_cli def test_to_cli_string(): @@ -17,3 +23,20 @@ def test_to_cli_string(): c(action="set", key="SECRET", value="a b", quote="always") == 'dotenv -q always set SECRET "a b"' ) + + +@pytest.mark.parametrize( + "directory", + ["app", "my app", "app's"], +) +@pytest.mark.parametrize("path_type", [str, Path]) +def test_to_cli_string_path(cli, tmp_path, directory, path_type): + project = tmp_path / directory + project.mkdir() + path = project / ".env" + path.write_text("DEBUG=True\n") + + command = c(path=path_type(path), action="get", key="DEBUG") + result = cli.invoke(dotenv_cli, shlex.split(command)[1:]) + + assert (result.exit_code, result.output) == (0, "True\n")