Skip to content

Commit b20d818

Browse files
venthurtheskumar
authored andcommitted
Add 'dotenv run' command for calling arbitrary shell script with .env (theskumar#105)
* Added support for calling arbitrary script with .env variables provides * Integrated everything into cli.py * Fail properly when no command given and exit with return code of command * Updated README * Added tests. * Fixed test for run w/o command
1 parent fe35be1 commit b20d818

File tree

4 files changed

+85
-1
lines changed

4 files changed

+85
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ Options:
192192
Commands:
193193
get Retrive the value for the given key.
194194
list Display all the stored key/value.
195+
run Run command with environment variables from .env file present
195196
set Store the given key/value.
196197
unset Removes the given key.
197198
```

dotenv/cli.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
'Run pip install "python-dotenv[cli]" to fix this.')
99
sys.exit(1)
1010

11-
from .main import dotenv_values, get_key, set_key, unset_key
11+
from .main import dotenv_values, get_key, set_key, unset_key, run_command
1212

1313

1414
@click.group()
@@ -78,5 +78,19 @@ def unset(ctx, key):
7878
exit(1)
7979

8080

81+
@cli.command(context_settings={'ignore_unknown_options': True})
82+
@click.pass_context
83+
@click.argument('commandline', nargs=-1, type=click.UNPROCESSED)
84+
def run(ctx, commandline):
85+
"""Run command with environment variables present."""
86+
file = ctx.obj['FILE']
87+
dotenv_as_dict = dotenv_values(file)
88+
if not commandline:
89+
click.echo('No command given.')
90+
exit(1)
91+
ret = run_command(commandline, dotenv_as_dict)
92+
exit(ret)
93+
94+
8195
if __name__ == "__main__":
8296
cli()

dotenv/main.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import re
99
import sys
10+
from subprocess import Popen, PIPE, STDOUT
1011
import warnings
1112
from collections import OrderedDict
1213

@@ -253,3 +254,46 @@ def load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False):
253254
def dotenv_values(dotenv_path=None, stream=None, verbose=False):
254255
f = dotenv_path or stream or find_dotenv()
255256
return DotEnv(f, verbose=verbose).dict()
257+
258+
259+
def run_command(command, env):
260+
"""Run command in sub process.
261+
262+
Runs the command in a sub process with the variables from `env`
263+
added in the current environment variables.
264+
265+
Parameters
266+
----------
267+
command: List[str]
268+
The command and it's parameters
269+
env: Dict
270+
The additional environment variables
271+
272+
Returns
273+
-------
274+
int
275+
The return code of the command
276+
277+
"""
278+
# copy the current environment variables and add the vales from
279+
# `env`
280+
cmd_env = os.environ.copy()
281+
cmd_env.update(env)
282+
283+
p = Popen(command,
284+
stdin=PIPE,
285+
stdout=PIPE,
286+
stderr=STDOUT,
287+
universal_newlines=True,
288+
bufsize=0,
289+
shell=False,
290+
env=cmd_env)
291+
try:
292+
out, _ = p.communicate()
293+
print(out)
294+
except Exception:
295+
warnings.warn('An error occured, running the command:')
296+
out, _ = p.communicate()
297+
warnings.warn(out)
298+
299+
return p.returncode

tests/test_cli.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,28 @@ def test_get_key_with_interpolation_of_unset_variable(cli):
192192
assert stored_value == 'BAR'
193193
del(environ['NOT_SET'])
194194
sh.rm(dotenv_path)
195+
196+
197+
def test_run(cli):
198+
with cli.isolated_filesystem():
199+
sh.touch(dotenv_path)
200+
sh.cd(here)
201+
dotenv.set_key(dotenv_path, 'FOO', 'BAR')
202+
result = sh.dotenv('run', 'printenv', 'FOO').strip()
203+
assert result == 'BAR'
204+
205+
206+
def test_run_with_other_env(cli, dotenv_file):
207+
cli.invoke(dotenv_cli, ['--file', dotenv_file, 'set', 'FOO', "BAR"])
208+
result = cli.invoke(dotenv_cli, ['--file', dotenv_file, 'run', 'printenv', 'FOO'])
209+
assert result.output.strip() == 'BAR'
210+
211+
212+
def test_run_without_cmd(cli):
213+
result = cli.invoke(dotenv_cli, ['run'])
214+
assert result.exit_code != 0
215+
216+
217+
def test_run_with_invalid_cmd(cli):
218+
result = cli.invoke(dotenv_cli, ['run', 'i_do_not_exist'])
219+
assert result.exit_code != 0

0 commit comments

Comments
 (0)