标签归档:automated-deploy

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

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

我想在本地运行我的结构脚本,这将依次登录到我的服务器,切换用户以进行部署,激活项目.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’中。

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

I want to run my fabric script locally, which will in turn, log into my server, switch user to deploy, activate the projects .virtualenv, which will change dir to the project and issue a git pull.

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

I typically use the workon command from virtualenvwrapper which sources the activate file and the postactivate file will put me in the project folder. In this case, it seems that because fabric runs from within shell, control is give over to fabric, so I can’t use bash’s source built-in to ‘$source ~/.virtualenv/myvenv/bin/activate’

Anybody have an example and explanation of how they have done this?


回答 0

现在,您可以做我所要做的事情,这很笨拙,但效果很好*(此用法假设您正在使用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字符等。

Right now, you can do what I do, which is kludgy but works perfectly well* (this usage assumes you’re using virtualenvwrapper — which you should be — but you can easily substitute in the rather longer ‘source’ call you mentioned, if not):

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

Since version 1.0, Fabric has a prefix context manager which uses this technique so you can for example:

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

* There are bound to be cases where using the command1 && command2 approach may blow up on you, such as when command1 fails (command2 will never run) or if command1 isn’t properly escaped and contains special shell characters, and so forth.


回答 1

作为对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')

As an update to bitprophet’s forecast: With Fabric 1.0 you can make use of prefix() and your own context managers.

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')

回答 2

我只是使用一个简单的包装函数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)

I’m just using a simple wrapper function virtualenv() that can be called instead of run(). It doesn’t use the cd context manager, so relative paths can be used.

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)

回答 3

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")

virtualenvwrapper can make this a little simpler

  1. Using @nh2’s approach (this approach also works when using local, but only for virtualenvwrapper installations where workon is in $PATH, in other words — 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. Or deploy your fab file and run this locally. This setup lets you activate the virtualenv for local or remote commands. This approach is powerful because it works around local‘s inability to run .bashrc using bash -l:

    @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")
    

回答 4

这是我在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')

This is my approach on using virtualenv with local deployments.

Using fabric’s path() context manager you can run pip or python with binaries from 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')

回答 5

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

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

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

Thanks to all answers posted and I would like to add one more alternative for this. There is an module, fabric-virtualenv, which can provide the function as the same code:

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

fabric-virtualenv makes use of fabric.context_managers.prefix, which might be a good way :)


回答 6

如果您想将软件包安装到环境中,或者要根据环境中的软件包运行命令,我发现此技巧可以解决我的问题,而不是编写复杂的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

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

If you want to install the packages to environment or want to run commands according to the packages you have in environment, I have found this hack to solve my problem, instead of writing complex methods of fabric or installing new OS packages:

/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

This way you might not need to activate the environment, but you can execute commands under the environment.


回答 7

以下是装饰器的代码,该代码将导致对任何运行/ 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")

Here is code for a decorator that will result in the use of Virtual Environment for any run/sudo calls:

# 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

and then to use the decorator, note the order of the decorators is important:

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

回答 8

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

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您的虚拟环境目录,并在适当的地方添加此方法。

This approach worked for me, you can apply this too.

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'")

Assuming venv is your virtual env directory and add this method wherever appropriate.