通过架构以部署用户身份激活virtualenv


130

我想在本地运行我的结构脚本,这将依次登录到我的服务器,切换用户以进行部署,激活项目.virtualenv,这将把dir更改为项目并发出git pull。

def git_pull():
    sudo('su deploy')
    # here i need to switch to the virtualenv
    run('git pull')

我通常使用来自virtualenvwrapper的workon命令,该命令提供激活文件,后激活文件会将我放在项目文件夹中。在这种情况下,似乎因为结构是在shell中运行的,所以控制权移交给了结构,所以我不能将bash的源内置到'$ source〜/ .virtualenv / myvenv / bin / activate'中。

有人举一个例子,并解释他们如何做到这一点吗?


1
出于好奇,您为什么不使用它workon作为prefix
Daniel C. Sobral

Answers:


96

现在,您可以做我所要做的事情,这很笨拙,但效果很好*(此用法假设您正在使用virtualenvwrapper-应该如此-但您可以轻松地替换为您提到的更长的“源”调用, 如果不):

def task():
    workon = 'workon myvenv && '
    run(workon + 'git pull')
    run(workon + 'do other stuff, etc')

从1.0版开始,Fabric具有使用此技术的prefix上下文管理器,因此您可以例如:

def task():
    with prefix('workon myvenv'):
        run('git pull')
        run('do other stuff, etc')

*在某些情况下,使用这种command1 && command2方法可能会炸毁您,例如command1失败时(command2永远不会运行)或command1无法正确转义并且包含特殊的shell字符等。


7
workon未知sh。我们如何告诉Fabric使用bash代替呢?
Pierre de LESPINAY 2013年

18
恕我直言,您应该使用source venv/bin/activate。它更容易并且可以直接使用。workon是附加的依赖项,即使已安装,也必须添加它.bashrc-对于结构部署而言太复杂了。
Dave Halter 2013年

@PierredeLESPINAY请参阅stackoverflow.com/questions/11272372/…,以获取针对您的问题的解决方案。
dukebody

137

作为对bitprophet预测的更新:使用Fabric 1.0,您可以使用prefix()和您自己的上下文管理器。

from __future__ import with_statement
from fabric.api import *
from contextlib import contextmanager as _contextmanager

env.hosts = ['servername']
env.user = 'deploy'
env.keyfile = ['$HOME/.ssh/deploy_rsa']
env.directory = '/path/to/virtualenvs/project'
env.activate = 'source /path/to/virtualenvs/project/bin/activate'

@_contextmanager
def virtualenv():
    with cd(env.directory):
        with prefix(env.activate):
            yield

def deploy():
    with virtualenv():
        run('pip freeze')

@simon,编写自己的前缀方法,该方法调用.bashrc并将前缀和命令都包装在bash的-c参数中。见下文
Dave

5
source未知sh。我们如何告诉Fabric使用bash代替呢?
Pierre de LESPINAY 2013年

2
您可以使用@PierredeLESPINAY .代替source
katy lavallee

cd()当您完全指定activatein 的路径时为什么使用prefix()
尼克T

@NickT因为prefix()似乎那里没有cd- 请参阅这些具有相同功能的文档。我们想要在cd那里,以便当我们yield执行其他命令时(pip freeze在我的示例中),这些命令可以相对于该目录。
NH2

18

我只是使用一个简单的包装函数virtualenv()而不是run()即可调用。它不使用cd上下文管理器,因此可以使用相对路径。

def virtualenv(command):
    """
    Run a command in the virtualenv. This prefixes the command with the source
    command.
    Usage:
        virtualenv('pip install django')
    """
    source = 'source %(project_directory)s/bin/activate && ' % env
    run(source + command)

9

virtualenvwrapper 可以简化一点

  1. 使用@ nh2的方法(该方法在使用时也适用local,但仅适用workon$PATHin中的virtualenvwrapper安装程序,换句话说-Windows)

    from contextlib import contextmanager
    from fabric.api import prefix
    
    @contextmanager
    def virtualenv():
        with prefix("workon env1"):
            yield
    
    def deploy():
        with virtualenv():
            run("pip freeze > requirements.txt")
  2. 或部署fab文件并在本地运行。通过此设置,您可以为本地或远程命令激活virtualenv。这种方法功能强大,因为它可以解决local无法使用bash -l以下命令运行.bashrc的问题:

    @contextmanager
    def local_prefix(shell, prefix):
        def local_call(command):
            return local("%(sh)s \"%(pre)s && %(cmd)s\"" % 
                {"sh": shell, "pre": prefix, "cmd": command})
        yield local_prefix
    
    def write_requirements(shell="/bin/bash -lic", env="env1"):
        with local_prefix(shell, "workon %s" % env) as local:
            local("pip freeze > requirements.txt")
    
    write_requirements()  # locally
    run("fab write_requirements")

感谢总结nh2的答案,在Python 2.6+上可以在5行中完成virtualenv contextmanager声明,但是永远不能保证'workon'别名总是正确导入,并且使用`source ... / activate'更为可靠。命令
Alex Volkov

8

这是我在virtualenv本地部署中使用的方法。

使用fabric的path()上下文管理器,您可以运行virtualenv pippython使用virtualenv中的二进制文件。

from fabric.api import lcd, local, path

project_dir = '/www/my_project/sms/'
env_bin_dir = project_dir + '../env/bin/'

def deploy():
    with lcd(project_dir):
        local('git pull origin')
        local('git checkout -f')
        with path(env_bin_dir, behavior='prepend'):
            local('pip freeze')
            local('pip install -r requirements/staging.txt')
            local('./manage.py migrate') # Django related

            # Note: previous line is the same as:
            local('python manage.py migrate')

            # Using next line, you can make sure that python 
            # from virtualenv directory is used:
            local('which python')

我非常喜欢这种方法-我认为这种方法没有任何明显的缺点,而且非常干净。谢谢:)
simon

仍然是最好,最干净的答案
n1_

4

感谢发布的所有答案,我想为此添加另一种替代方法。有一个模块fabric-virtualenv,可以提供与相同代码相同的功能:

>>> from fabvenv import virtualenv
>>> with virtualenv('/home/me/venv/'):
...     run('python foo')

fabric-virtualenv使用fabric.context_managers.prefix,这可能是一个好方法:)


有趣,但我不喜欢没有链接到SCM /问题跟踪器的事实。仅在PYPI上发布但没有链接到源代码和问题跟踪程序的软件包不会引起人们的信任....但是易于修复。
索林

2

如果您想将软件包安装到环境中,或者要根据环境中的软件包运行命令,我发现此技巧可以解决我的问题,而不是编写复杂的Fabric方法或安装新的OS软件包:

/path/to/virtualenv/bin/python manage.py migrate/runserver/makemigrations  # for running commands under virtualenv

local("/home/user/env/bin/python manage.py migrate")    # fabric command


/path/to/virtualenv/bin/pip install -r requirements.txt   # installing/upgrading virtualenv

local("/home/user/env/bin/pip install -r requirements.txt")  #  fabric command

这样,您可能不需要激活环境,但是可以在该环境下执行命令。


1

以下是装饰器的代码,该代码将导致对任何运行/ sudo调用使用虚拟环境:

# This is the bash code to update the $PATH as activate does
UPDATE_PYTHON_PATH = r'PATH="{}:$PATH"'.format(VIRTUAL_ENV_BIN_DIR)

def with_venv(func, *args, **kwargs):
  "Use Virtual Environment for the command"

  def wrapped(*args, **kwargs):
    with prefix(UPDATE_PYTHON_PATH):
      return func(*args, **kwargs)

  wrapped.__name__ = func.__name__
  wrapped.__doc__ = func.__doc__
  return wrapped

然后要使用装饰器,请注意装饰器的顺序很重要:

@task
@with_venv
def which_python():
  "Gets which python is being used"
  run("which python")

1

这种方法对我有用,您也可以应用。

from fabric.api import run 
# ... other code...
def install_pip_requirements():
    run("/bin/bash -l -c 'source venv/bin/activate' "
        "&& pip install -r requirements.txt "
        "&& /bin/bash -l -c 'deactivate'")

假定venv您的虚拟环境目录,并在适当的地方添加此方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.