From 2e249c34843317f410a5beb9f8d85947b08e97fb Mon Sep 17 00:00:00 2001 From: Nat Noordanus Date: Sat, 18 Jun 2022 20:46:47 +0200 Subject: [PATCH] Make envfile global and task level options also accept a list of values --- README.rst | 25 ++++++++++++++++--- poethepoet/config.py | 2 +- poethepoet/env/manager.py | 13 +++++++--- poethepoet/task/base.py | 2 +- tests/fixtures/envfile_project/first.env | 6 +++++ tests/fixtures/envfile_project/fourth.env | 2 ++ .../envfile_project/multiple_envfiles.toml | 10 ++++++++ tests/fixtures/envfile_project/second.env | 5 ++++ tests/fixtures/envfile_project/third.env | 3 +++ tests/test_envfile.py | 21 ++++++++++++++++ 10 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 tests/fixtures/envfile_project/first.env create mode 100644 tests/fixtures/envfile_project/fourth.env create mode 100644 tests/fixtures/envfile_project/multiple_envfiles.toml create mode 100644 tests/fixtures/envfile_project/second.env create mode 100644 tests/fixtures/envfile_project/third.env diff --git a/README.rst b/README.rst index e3bfaaff..2c4e56cd 100644 --- a/README.rst +++ b/README.rst @@ -410,7 +410,10 @@ set by replacing the last line with the following: serve.env.PORT.default = "9001" -You can also specify an env file (with bash-like syntax) to load per task like so: +Loading env vars from an env file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also specify one or more env files (with bash-like syntax) to load per task like so: .. code-block:: bash @@ -424,9 +427,21 @@ You can also specify an env file (with bash-like syntax) to load per task like s serve.script = "myapp:run" serve.envfile = ".env" -It it also possible to reference existing env vars when defining a new env var for a +The envfile option accepts the name (or relative path) to a single envfile as shown +above but can also by given a list of such paths like so: + +.. code-block:: toml + + serve.envfile = [".env", "local.env"] + +In this case the referenced files will be loaded in the given order. + +Defining env vars in terms of other env vars +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is also possible to reference existing env vars when defining a new env var for a task. This may be useful for aliasing or extending a variable already defined in the -host environment, globally in the config, or in a referencd envfile. In the following +host environment, globally in the config, or in a referenced envfile. In the following example the value from $TF_VAR_service_port on the host environment is also made available as $FLASK_RUN_PORT within the task. @@ -434,7 +449,7 @@ available as $FLASK_RUN_PORT within the task. [tool.poe.tasks.serve] serve.cmd = "flask run" - serve.env = { FLASK_RUN_PORT = "${TF_VAR_service_port}" } + serve.env = { FLASK_RUN_PORT = "${TF_VAR_service_port}" } Declaring CLI arguments @@ -711,6 +726,8 @@ You can also specify an env file (with bash-like syntax) to load for all tasks l [tool.poe] envfile = ".env" +The envfile global option also accepts a list of env files. + Default command verbosity ------------------------- diff --git a/poethepoet/config.py b/poethepoet/config.py index cad0e5b5..e209161c 100644 --- a/poethepoet/config.py +++ b/poethepoet/config.py @@ -28,7 +28,7 @@ class PoeConfig: "default_array_task_type": str, "default_array_item_task_type": str, "env": dict, - "envfile": str, + "envfile": (str, list), "executor": dict, "include": (str, list), "poetry_command": str, diff --git a/poethepoet/env/manager.py b/poethepoet/env/manager.py index 374f3e35..46200906 100644 --- a/poethepoet/env/manager.py +++ b/poethepoet/env/manager.py @@ -42,8 +42,12 @@ def __init__( if parent_env is None: # Get env vars from envfile referenced in global options - if self._config.global_envfile is not None: - self._vars.update(self.envfiles.get(self._config.global_envfile)) + global_envfile = self._config.global_envfile + if isinstance(global_envfile, str): + self._vars.update(self.envfiles.get(global_envfile)) + elif isinstance(global_envfile, list): + for task_envfile_path in global_envfile: + self._vars.update(self.envfiles.get(task_envfile_path)) # Get env vars from global options self._apply_env_config(self._config.global_env) @@ -80,8 +84,11 @@ def for_task( result = EnvVarsManager(self._config, self._ui, parent_env=self) # Include env vars from envfile referenced in task options - if task_envfile is not None: + if isinstance(task_envfile, str): result.update(self.envfiles.get(task_envfile)) + elif isinstance(task_envfile, list): + for task_envfile_path in task_envfile: + result.update(self.envfiles.get(task_envfile_path)) # Include env vars from task options if task_env is not None: diff --git a/poethepoet/task/base.py b/poethepoet/task/base.py index 83723436..e0d2af77 100644 --- a/poethepoet/task/base.py +++ b/poethepoet/task/base.py @@ -60,7 +60,7 @@ class PoeTask(metaclass=MetaPoeTask): "capture_stdout": (str), "deps": list, "env": dict, - "envfile": str, + "envfile": (str, list), "executor": dict, "help": str, "uses": dict, diff --git a/tests/fixtures/envfile_project/first.env b/tests/fixtures/envfile_project/first.env new file mode 100644 index 00000000..fe516647 --- /dev/null +++ b/tests/fixtures/envfile_project/first.env @@ -0,0 +1,6 @@ +VAR_A=VAL_A +VAR_B=NOT_B +VAR_C=NOT_C +VAR_D=NOT_D +VAR_E=NOT_E +VAR_F=NOT_F diff --git a/tests/fixtures/envfile_project/fourth.env b/tests/fixtures/envfile_project/fourth.env new file mode 100644 index 00000000..4faf077f --- /dev/null +++ b/tests/fixtures/envfile_project/fourth.env @@ -0,0 +1,2 @@ +VAR_E=VAL_E +VAR_F=NOT_F diff --git a/tests/fixtures/envfile_project/multiple_envfiles.toml b/tests/fixtures/envfile_project/multiple_envfiles.toml new file mode 100644 index 00000000..67ece27c --- /dev/null +++ b/tests/fixtures/envfile_project/multiple_envfiles.toml @@ -0,0 +1,10 @@ +[tool.poe] +envfile = ["first.env", "second.env"] +env = { VAR_C = "VAL_C" } + +[tool.poe.tasks.show_me_the_vals] +cmd = """ +poe_test_echo "${VAR_A}-${VAR_B}-${VAR_C}-${VAR_D}-${VAR_E}-${VAR_F}!!" +""" +envfile = ["third.env", "fourth.env"] +env = { VAR_F = "VAL_F" } diff --git a/tests/fixtures/envfile_project/second.env b/tests/fixtures/envfile_project/second.env new file mode 100644 index 00000000..562736f0 --- /dev/null +++ b/tests/fixtures/envfile_project/second.env @@ -0,0 +1,5 @@ +VAR_B=VAL_B +VAR_C=NOT_C +VAR_D=NOT_D +VAR_E=NOT_E +VAR_F=NOT_F diff --git a/tests/fixtures/envfile_project/third.env b/tests/fixtures/envfile_project/third.env new file mode 100644 index 00000000..a1866afa --- /dev/null +++ b/tests/fixtures/envfile_project/third.env @@ -0,0 +1,3 @@ +VAR_D=VAL_D +VAR_E=NOT_E +VAR_F=NOT_F diff --git a/tests/test_envfile.py b/tests/test_envfile.py index 367db5e5..99ac61a2 100644 --- a/tests/test_envfile.py +++ b/tests/test_envfile.py @@ -38,3 +38,24 @@ def test_task_envfile_and_default(run_poe_subproc, is_windows): ) assert result.stdout == "deploying to admin:12345@prod.example.com/app\n" assert result.stderr == "" + + +def test_multiple_envfiles(run_poe_subproc, projects, is_windows): + result = run_poe_subproc( + f'--root={projects["envfile/multiple_envfiles"]}', "show_me_the_vals" + ) + + if is_windows: + assert ( + 'Poe => poe_test_echo "VAL_A-VAL_B-VAL_C-VAL_D-VAL_E-VAL_F!!"\n' + in result.capture + ) + assert result.stdout == '"VAL_A-VAL_B-VAL_C-VAL_D-VAL_E-VAL_F!!"\n' + assert result.stderr == "" + else: + assert ( + "Poe => poe_test_echo VAL_A-VAL_B-VAL_C-VAL_D-VAL_E-VAL_F!!\n" + in result.capture + ) + assert result.stdout == "VAL_A-VAL_B-VAL_C-VAL_D-VAL_E-VAL_F!!\n" + assert result.stderr == ""