43

I would like to pass a few values from fabric into the remote environment, and I'm not seeing a great way to do it. The best I've come up with so far is:

with prefix('export FOO=BAR'):
    run('env | grep BAR')

This does seem to work, but it seems like a bit of a hack.

I looked in the GIT repository and it looks like this is issue #263.

David Parmenter
  • 535
  • 1
  • 4
  • 8
  • 1
    But will the variables persist after fabric closes the connection? From the documentation (`prefix` and `shell_env`) I understand that the environment variables will be set for the wrapped commands only. – glarrain Jul 17 '13 at 19:10

5 Answers5

61

As of fabric 1.5 (released), fabric.context_managers.shell_env does what you want.

    with shell_env(FOO1='BAR1', FOO2='BAR2', FOO3='BAR3'):
        local("echo FOO1 is $FOO1")
Day
  • 9,465
  • 6
  • 57
  • 93
Shanmu
  • 938
  • 7
  • 15
  • 2
    Doesn't seem to work on Windows. Support has been added, but it's not in latest release (1.6). – Tuukka Mustonen May 22 '13 at 11:12
  • But will the variables persist after fabric closes the connection? From the documentation I understand that the environment variables will be set for the wrapped commands only. – glarrain Jul 17 '13 at 19:09
  • @glarrain yes, that's the point of them implementing it as a context manager – Anentropic Dec 23 '14 at 08:58
  • @Anentropic then wouldn't that be the exact opposite, that when the context manager exits the variables are cleared? – glarrain Dec 30 '14 at 15:59
  • Why putting `LocalCommand export APP_ENV="staging"` in ~/.ssh/config doesn't work? – Hussain Oct 16 '17 at 07:14
11

I think your prefix-based solution is perfectly valid. However, if you want to have a shell_env context manager as the one proposed in issue#263, you can use the following alternative implementation in your fab files:

from fabric.api import run, env, prefix
from contextlib import contextmanager

@contextmanager
def shell_env(**env_vars):
    orig_shell = env['shell']
    env_vars_str = ' '.join('{0}={1}'.format(key, value)
                           for key, value in env_vars.items())
    env['shell']='{0} {1}'.format(env_vars_str, orig_shell)
    yield
    env['shell']= orig_shell

def my_task():
    with prefix('echo FOO1=$FOO1, FOO2=$FOO2, FOO3=$FOO3'):
        with shell_env(FOO1='BAR1', FOO2='BAR2', FOO3='BAR3'):
            run('env | grep BAR')

Note that this context manager modifies env['shell'] instead of env['command_prefixes'] (as prefix context manager does), so you:

  • can still use prefix (see example output below) without the interaction problems mentioned in issue#263.
  • have to apply any changes to env['shell'] before using shell_env. Otherwise, shell_env changes will be overwritten and environment variables won't be available for your commands.

When executing the fab file above, you get the following output:

$ fab -H localhost my_task
[localhost] Executing task 'my_task'
[localhost] run: env | grep BAR
[localhost] out: FOO1=BAR1, FOO2=BAR2, FOO3=BAR3
[localhost] out: FOO1=BAR1
[localhost] out: FOO2=BAR2
[localhost] out: FOO3=BAR3
[localhost] out: 

Done.
Disconnecting from localhost... done.
jcollado
  • 39,419
  • 8
  • 102
  • 133
  • 1
    I've been using this technique, but I've noticed in later versions of fabric it no longer works. Instead, you should/must use the fabric.context_managers.shell_env implementation, which does work. – Jason R. Coombs Jun 14 '15 at 02:31
2

Fabric 1.5.0 (currently in Git) takes shell as local() named argument. If you pass '/bin/bash' there it passes it to executable argument of Popen.

It won't execute your .bashrc though because .bashrc is sourced on interactive invocation of bash. You can source any file you want inside local:

local('. /usr/local/bin/virtualenvwrapper.sh && workon focus_tests && bunch local output', shell='/bin/bash')
peroksid
  • 890
  • 6
  • 17
0

Another way is to pass a value through command line with --set:

--set=domain=stackoverflow.com 

Then, you can address to it in script with env.domain

see http://docs.fabfile.org/en/1.11/usage/fab.html#cmdoption--set

Vasili Pascal
  • 3,102
  • 1
  • 27
  • 21
0

Try using decorator

from fabric.context_managers import shell_env
from functools import wraps


def set_env():
    def decorator(func):
        @wraps(func)
        def inner(*args, **kwargs):
            with shell_env(DJANGO_CONFIGURATION=env.config):
                run("echo $DJANGO_CONFIGURATION")
                return func(*args, **kwargs)
        return inner
    return decorator


@task
@set_env()
def testme():
    pass