Skip to content

Commit

Permalink
support multiple dotenv files
Browse files Browse the repository at this point in the history
closes: #113
  • Loading branch information
venthur committed Aug 6, 2024
1 parent 3ea6dc1 commit ff86204
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [unreleased]

* support multiple .env files via `-e` or `--dotenv` parameters

## [3.3.1] -- 2024-07-13

* renamed debian package to dotenv-cli
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ the current directory, puts the contents in the environment by either changing
existing- or adding new environment variables, and executes the given command.

`dotenv` supports alternative `.env` files like `.env.development` via the `-e`
or `--dotenv` parameters.
or `--dotenv` parameters. This parameter can be repeated to load multiple
files, the .env files will be loaded in the order they are provided.

With the `--replace` flag, `dotenv` also provides an option to completely
replace the environment variables with the ones from the `.env` file, allowing
Expand Down
3 changes: 2 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ the current directory, puts the contents in the environment by either changing
existing- or adding new environment variables, and executes the given command.

`dotenv` supports alternative `.env` files like `.env.development` via the `-e`
or `--dotenv` parameters.
or `--dotenv` parameters. This parameter can be repeated to load multiple
files, the .env files will be loaded in the order they are provided.

With the `--replace` flag, `dotenv` also provides an option to completely
replace the environment variables with the ones from the `.env` file, allowing
Expand Down
11 changes: 9 additions & 2 deletions dotenv_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ def parse_args(args: list[str] | None = None) -> argparse.Namespace:
parser.add_argument(
"-e",
"--dotenv",
help="alternative .env file",
default=".env",
help=(
"alternative .env file; this parameter can be provided multiple "
"times and the .env files will be evaluated in order"
),
action="append",
default=[".env"],
)

parser.add_argument(
Expand Down Expand Up @@ -78,6 +82,9 @@ def main() -> NoReturn | int:
"""
args = parse_args()
# if alternative .env file is given, remove the default one
if len(args.dotenv) > 1:
args.dotenv = args.dotenv[1:]
if not args.command:
return 0

Expand Down
12 changes: 7 additions & 5 deletions dotenv_cli/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def read_dotenv(filename: str) -> dict[str, str]:


def run_dotenv(
filename: str, command: list[str], replace: bool = False
filenames: list[str], command: list[str], replace: bool = False
) -> NoReturn | int:
"""Run dotenv.
Expand All @@ -83,8 +83,8 @@ def run_dotenv(
Parameters
----------
filename
path to the .env file
filenames
paths to the .env files
command
command to execute
replace_env
Expand All @@ -97,8 +97,10 @@ def run_dotenv(
function does not return normally.
"""
# read dotenv
dotenv = read_dotenv(filename)
# read dotenv files
dotenv = {}
for filename in filenames:
dotenv.update(read_dotenv(filename))

if replace:
# replace env
Expand Down
33 changes: 33 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,39 @@ def test_alternative_dotenv() -> None:
assert b"foo=bar" in proc.stdout


def test_multiple_dotenv() -> None:
"""Test multiple dotenv files."""
with tempfile.NamedTemporaryFile("w", delete=False) as f:
f.write("foo=foo")

with tempfile.NamedTemporaryFile("w", delete=False) as b:
b.write("bar=bar")

proc = run(["dotenv", "-e", f.name, "-e", b.name, "env"], stdout=PIPE)
assert b"foo=foo" in proc.stdout
assert b"bar=bar" in proc.stdout


def test_multiple_dotenv_order() -> None:
"""Test multiple dotenv files are processed in correct order."""
with tempfile.NamedTemporaryFile("w", delete=False) as f1:
f1.write("foo=1")

with tempfile.NamedTemporaryFile("w", delete=False) as f2:
f2.write("foo=2")

proc = run(["dotenv", "-e", f1.name, "-e", f2.name, "env"], stdout=PIPE)
assert b"foo=2" in proc.stdout
assert b"foo=1" not in proc.stdout

proc = run(
["dotenv", "-e", f1.name, "-e", f2.name, "-e", f1.name, "env"],
stdout=PIPE
)
assert b"foo=1" in proc.stdout
assert b"foo=2" not in proc.stdout


def test_nonexisting_dotenv() -> None:
"""Test non-existing dotenv file."""
proc = run(["dotenv", "-e", "/tmp/i.dont.exist", "true"], stderr=PIPE)
Expand Down

0 comments on commit ff86204

Please sign in to comment.