pyinvoke / invoke

Pythonic task management & command execution.
http://pyinvoke.org
BSD 2-Clause "Simplified" License
4.42k stars 370 forks source link

Allow multiple `--config`/`-f` options #453

Open raphael-proust opened 7 years ago

raphael-proust commented 7 years ago

Currently, invoke only accepts one --config/-f option per invokation.

Multiple config files can be useful to mix configuration from multiple sources. E.g., a general configuration file and then either one for test or one for making more optimisations. leading to two distinct invokations:

invoke -f common.json -f with-tests.json build
invoke -f common.json -f without-tests.json build

These two configurations pass different options to multiple compilers.


Workaround: it is possible to manually merge configuration files (i.e., placing the values from common.json into both with-tests.json and without-tests.json). This is unsatisfying because it leads to duplicated information, maintenance costs, etc.


Invoke is capable of merging multiple json config files; specifically, as per documentation:

However, invoke refuses multiple --config/-f options.


Solution: merge the files as given with -f in the order that they appear in the command line (i.e., later files overwrite earlier files).


How to reproduce:

$ cat tasks.py 
from invoke import task

@task
def build(ctx):
    print('ok')
$ cat a.json 
{
    "a": "a"
}
$ cat b.json 
{
    "b": 0
}
$ invoke -f a.json -f b.json build
No idea what 'b.json' is!
bitprophet commented 7 years ago

So, tl;dr, allow >1 runtime configuration level? Seems reasonable enough at a glance; thanks for the feature request!. I see two obvious ways to accomplish it:

Not sure which I personally like more but I am leaning towards the second option.

raphael-proust commented 7 years ago

Yes, that's the essence of the issue.

I don't know the internals of pyinvoke enough to make any form of decision about how to go about it. As a user of pyinvoke, I'd be satisfied if the list of configuration source was extended by replacing the single -f configuration with multiple of them.

Basically, if I have to think about the series of -f options the same way I think about the series of configuration sources.

Sorry if I don't use the standard vocabulary to describe what I describe. I'm a recent user of pyinvoke.

bitprophet commented 7 years ago

No worries, the internals notes in my comment were mostly notes-to-self :)

Also, just realized this implicitly blocks on #132 - at the moment we don't actually support giving a flag >1 time, but it's something we definitely need to have.

tedmiston commented 5 years ago

I came here after discovering passing multiple runtime configuration files wasn't currently supported as well.

For my use case, as an example I have config files associated with foo objects and also with bar objects.

It would be nice to be able to do something like:

$ inv -f foos/foo1.yaml -f bars/bar1.yaml ...
$ inv -f foos/foo1.yaml -f bars/bar2.yaml ...
$ inv -f foos/foo2.yaml -f bars/bar1.yaml ...
$ inv -f foos/foo2.yaml -f bars/bar2.yaml ...
...

In my case it would be equally sufficient, or even preferable, to be able to do something like:

$ inv --foo=foo1 --bar=bar2 ...

and then have that dynamically load both config files foos/foo1.yaml and bars/bar2.yaml behind the scenes and merge them. I read through the configuration docs, but could not really figure out how to achieve this use case.

For now I'm just going to make one file for each config combination permutation, but with several variations each of 3+ dimensions, it becomes non-ideal fast.

If it's possible to have the config files reference other config files that get pulled in, that would be nice, e.g.:

$ inv -f baz.yaml ...

baz.yaml:

foo: foos/foo1.yaml
bar: bars/bar2.yaml

Please let me know if there is a way to do this that I might be overlooking.

schperplata commented 2 years ago

@bitprophet Would this also apply to other settings like --collection? I would like to keep config in python files (tasks.py), but I would like to have one tasks.py shared across multiple projects (same tasks for multiple projects) and than optionally add tasks in project-specific tasks.py root dir. If --collection argument could be passed multiple times, I would just do:
invoke -c path/to/tasks1.py -c path/to/tasks2.py <taskname>