Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/dotenv/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,21 @@ def unset(ctx: click.Context, key: Any) -> None:
sys.exit(1)


@cli.command(context_settings={"ignore_unknown_options": True})
@cli.command(
context_settings={
"allow_extra_args": True,
"allow_interspersed_args": False,
"ignore_unknown_options": True,
}
)
@click.pass_context
@click.option(
"--override/--no-override",
default=True,
help="Override variables from the environment file with those from the .env file.",
)
@click.argument("commandline", nargs=-1, type=click.UNPROCESSED)
def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
def run(ctx: click.Context, override: bool, commandline: tuple[str, ...]) -> None:
"""Run command with environment variables present."""
file = ctx.obj["FILE"]
if not os.path.isfile(file):
Expand All @@ -180,7 +186,8 @@ def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
if not commandline:
click.echo("No command given.")
sys.exit(1)
run_command(commandline, dotenv_as_dict)

run_command([*commandline, *ctx.args], dotenv_as_dict)


def run_command(command: List[str], env: Dict[str, str]) -> None:
Expand Down
44 changes: 43 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import subprocess
from pathlib import Path
from typing import Optional
from typing import Optional, Sequence

import pytest
import sh
Expand All @@ -10,6 +11,21 @@
from dotenv.version import __version__


def invoke_sub(args: Sequence[str]) -> subprocess.CompletedProcess:
"""
Invoke the `dotenv` CLI in a subprocess.

This is necessary to test subcommands like `dotenv run` that replace the
current process.
"""

return subprocess.run(
["dotenv", *args],
capture_output=True,
text=True,
)


@pytest.mark.parametrize(
"output_format,content,expected",
(
Expand Down Expand Up @@ -249,3 +265,29 @@ def test_run_with_version(cli):

assert result.exit_code == 0
assert result.output.strip().endswith(__version__)


def test_run_with_command_flags(dotenv_path):
"""
Check that command flags passed after `dotenv run` are not interpreted.

Here, we want to run `printenv --version`, not `dotenv --version`.
"""

result = invoke_sub(["--file", dotenv_path, "run", "printenv", "--version"])

assert result.returncode == 0
assert result.stdout.strip().startswith("printenv ")


def test_run_with_dotenv_and_command_flags(cli, dotenv_path):
"""
Check that dotenv flags supersede command flags.
"""

result = invoke_sub(
["--version", "--file", dotenv_path, "run", "printenv", "--version"]
)

assert result.returncode == 0
assert result.stdout.strip().startswith("dotenv, version")