Add command line flag for setting env vars #13

Closed
bitprophet opened this Issue Aug 19, 2011 · 11 comments

Projects

None yet

3 participants

@bitprophet
Member

Description

This used to be called let and one could actually reimplement it with a task, since it used similar syntax to the current task argument stuff (let:foo=bar).

However, since I don't want to provide "builtin" tasks, best to do this as a flag, e.g. --set or --env or something. Probably make it repeatable and set a single var each time.

Has same outstanding issue as task arguments re: setting non-string values (#69).


Originally submitted by Jeff Forcier (bitprophet) on 2009-07-21 at 11:44am EDT

@bitprophet bitprophet was assigned Aug 19, 2011
@robcowie
robcowie commented Oct 5, 2011

Do you have any thoughts on how this should behave if a key-value pair is provided in --env that already exists in the env, i.e. was provided via one of the existing supported command line args (i.e. --user)?

For example. Given fab --user=robc --env=enc=utf8,user=bob, would the outcome be env['user'] == 'robc', env['user'] == 'bob' or should an error be thrown.

I think I favour overriding the existing env value ('robc') with the later value ('bob').

@bitprophet
Member

That's kind of tough. My instinct is the opposite of yours; I feel that the "specific" env-var-setting option should take precedence over the "generic" one.

Possible rationale: if somebody were actually in this situation and paying attention, they would've removed one or the other. Since they're now obviously not paying attention, they probably accidentally left the --env overwrite in by mistake. It's easier to notice "I am explicitly setting --user" (and thus, by leaving it in, acknowledging that you meant that user value) than it is to notice a user=xxx which is probably mixed in with a number of other x=y pairs.

@robcowie
robcowie commented Oct 7, 2011

Hard to argue with that logic :)

I had in my mind a scenario where a 'standard' fab command is used, and the --env parameter is pulled from some other source, or generated by some other script. Something along the lines of

echo 'enc=utf8,user=bob' > env.txt
fab --user=robc --env=`cat env.txt`

In this case, the value provided to --user is the default, and the value from the derived source would be expected to override it. (Note that I'm not sure if that command would even work in a terminal, just there as illustration).

To be honest I think your argument has it. I'm also not sure it's that big a deal either way, as long as the behaviour is documented. And it's not unreasonable to expect the user to ensure their args don't conflict.

@amorton
amorton commented Nov 4, 2011

I think this issue scratches my itch.

I need to deploy the same system to various facilities. My solution was have a yaml file for each that stores the roledefs for the facility. Then say something like.

fab deploy --facility=NY

And load the appropriate role defs on demand based on the facility.

To do that I need a way to supply a script wide argument. My hacky work around is to use fabrc files that define the facility env var.

fab deploy --config NY

The NY config file would only contain:

facility = NY

With this suggestion I could do it with.

fab deploy --env=facility=NY

I don't have much experience with the code base but would be happy to take a look of you think it's a straight forward change.
Thanks.

@amorton
amorton commented Nov 4, 2011

I may have engaged my keyboard before my brain.

For my situation a config file is probably the best way to go, it can look like this:

target_env=dev
# role=node
role_nodes=node1,node2,node3
role_database=node1
role_web=node2,node3

I can then use callables in the roledefs to pull from the env.

Still happy to lend a hand if you need it, I'm enjoying using fabric.

@bitprophet
Member

@amorton Keep in mind that fabricrc is mad limited and it's almost always better to simply import a real Python module into your fabfile instead (then refer to its members in your fabfile's e.g. env.roledefs.) We'll almost definitely drop fabricrc for a recommendation of the latter in 2.0; or use some sort of "Python module w/ extra sauce" version of same.

Being able to tweak env vars from the CLI is still a valid feature to add, but for "config" type data, Python imports are always going to be more flexible than using flags :)

@amorton
amorton commented Nov 5, 2011

Good point about using an python import rather than config file.

It still leaves issue of supplying some information via the command line to identify the target env. In the short term I could supply "NY" as a role via the command line and then have callables in env.roledefs that look for facility names in the env.roles and return the nodes in the facility.

Thanks again.

@bitprophet
Member

@amorton -- Most folks currently solve that with a simple task that sets the appropriate env var(s). E.g.

def ny():
    env.facility = "NY"

invoked as fab ny deploy.

Or:

def facility(which):
    env.facility = which.uppercase()

invoked as fab facility:ny deploy.

@amorton
amorton commented Nov 22, 2011

Thanks, thats mucho better. I didn't think about chaining the tasks like that.

@bitprophet
Member

I'm experimenting with this now: fab --set foo=bar is equivalent to env.foo = "bar", and fab --set foo is equivalent to env.foo = True. (This is to allow a common case of setting a default-False boolean setting to True.)

It may be specified >1 time (as in, fab --set x=y --set a=b) using normal optparse mechanisms for this. Not 100% sure whether this is better than manually splitting on commas, given that commas are an established pattern elsewhere.

It explicitly runs before any other flags are parsed, allowing any specific env-setting options to override anything updated with this one, as discussed above re: having specific override generic.


While the True-setting bit works, it's still avoiding a full implementation of #69. I feel this is a worthwhile temporary workaround because there is no other obvious valid interpretation of --set foo. Furthermore, users may still explicitly set something to an empty string (i.e. updating a default-nonempty-string value to be an empty string) with --set foo= (note the equals sign.)

This also works reasonably well for setting something to a False-equivalent value, provided one is testing that variable with if env.varname:.


@bitprophet
Member

Now thinking commas work best vs multiple flags:

  • More consistent with the rest of fabric, such as -H and CLI kwargs
  • Less typing for the non-base case
  • @wraithan claims that optparse's multiple-flags setup is non Unixy ;)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment