rvesse / airline

Java annotation-based framework for parsing Git like command line structures with deep extensibility
https://rvesse.github.io/airline/
Apache License 2.0
128 stars 20 forks source link

Support for simple config files #49

Open stephenh opened 8 years ago

stephenh commented 8 years ago

I have a project that using airline, and it works really well. A few of the command args are pretty short and sweet, e.g. hostname, port, but a few are potentially longer, e.g. a list of globs to whitelist/backlist files that the command processes, e.g.:

foo -h host -p port --exclude build,bin,whatever --include '*.txt,src,baz'

For just a few exclude/include args, it's not a big deal, but it seems like it would quickly become cumbersome, especially if the user invokes it on a regular basis, so I'd like to have an option to use a .foorc config file, with some sort of syntax like:

foo.exclude = build,bin,whatever
foo.include = *.txt,src,bar

Musing about what would be simplest for my program, it would be pretty awesome if airline could use the same @Option config I've setup for the CLI arg parsing to also pull in an optional config file.

Does this seem like a good idea? Or too much out of scope? If so, any hints/ideas about other ways/libraries you've seen that might do this?

rvesse commented 8 years ago

I like this idea though for something that sounds simple I think there is a lot of hidden complexity and open questions.

The first of which is how such a configuration file is located. The simple solution would be to search working directory and just use that. However I could envisage scenarios where you might want to specify multiple possible locations. The really awkward scenario is it if you wanted to allow users to specify an option specifying the location of that file because then you can get into a real chicken and egg scenario i.e. You have to parse the options in order to locate the file to parse the options.

Then there is the question of the property naming. For simple examples like yours this would be obvious but in my day job we have production usages of this library where we have 20+ commands in our Application. Some options are shared between many commands, others are very specific, and in some cases the same name is used four options interpreted very differently between commands. Perhaps a sensible solution would be to require Long format option names for any option you wish to specify in this manner.

Ideally I guess you would want to be able to customise the property names? Again in my day job production use of this library we have a single configuration file shared by several components of the product and it contains properties for those different components distinguished by different property name prefixes. So short of customising names in detail customising the name prefix would probably be a useful feature.

Another consideration is how would this behave if the user specifies an option both in the configuration file and at the command line? Does one take precedence, do they both apply, is they both apply which applies first - this would be significant for options which are not collection types as one value would override the other.

Again to take an example from my own use of this library, we allow some options to optionally be specified in configurations file. However we explicitly state that if the user also specifies it directly at the command line then that takes precedence.

In terms of implementation detail the main question would be where in the parsing process this happens. Logically it should probably happen at the start of parsing similar to how aliases are resolved by simply expanding the command line arguments to include values from the configuration file.

To sum up yes I definitely think this is in scope though the designs needs fleshing out a bit more.

Equally you could probably write something yourself in your own code, provided that your use case is relatively simple and you don't expect the open questions I have outlined to be of concern to you.

rvesse commented 8 years ago

One simple DIY option would be to provide an invoke command e.g.

invoke foo args.rc

Which would read in the given configuration file and then invoke the foo command with the arguments found in there

stephenh commented 8 years ago

configuration file is located

Agreed; I would envision something like:

question of the property naming

I had assumed something like <command>.<name> would work, e.g. imagine a .gitconfig, it could either be flat, e.g.:

Or ini style, e.g. each command is a group:

[reset]
foo = 1
bar = 2

[checkout]
baz = 3

I don't know much about shared properties to say much beyond I'd assumed that requiring everything to be command-scoped would at least disambiguate things. If you had foo shared by 4 commands, I think for the v1/initial pass, you'd have to just set it 4 times, once for each command...

Ideally I guess you would want to be able to customise the property names

I would punt on that, at least initially, and just assume the command name and property name as used in the CLI/annotations was the same for the config file. If anything, I think having the config file names drift from the CLI names would cause user confusion, in addition to the added implementation complexity.

Having one config file read by multiple separate systems (not just separate commands) seems like a boundary case that most people wouldn't run in to. That said, maybe if the standard config format was command.foo = 1, apps that share their config file with other apps could opt-in to an app-specific prefix, so it'd become app.command.foo = 1.

if the user also specifies it directly at the command line then that takes precedence.

Agreed.

Eventually it might be nice for lists to be smart, e.g. maybe the config file has command.foo = 1,2,3 and some sort of syntax like --foo+=4 would not overwrite 1,2,3 but add to it. But not for the first pass...

Logically it should probably happen at the start of parsing similar to how aliases are resolved by simply expanding the command line arguments to include values from the configuration file.

Agreed that seems like a good slice point. I haven't looked at how aliases are handled yet.

This is for a hobby project of mine, so I'm not sure when/if I'll get a chance to hack on it. I know I definitely want to have a config file at some point, so it just depends on when I get to it, and how ambitious I'm feeling if I add it to airline, or just code it one off in my app. At this point I think I'd at least start with an airline-based approach and see how it goes...

rvesse commented 6 years ago

Done some work lately on adding an abstraction layer - ResourceLocator - that could be used to make it easier to automatically configure how the configuration file would be located. I would still like to get to this feature eventually.