Активируйте virtualenv через фабрику как пользователь развертывания

130

Я хочу запустить свой сценарий фабрики локально, который, в свою очередь, войдет на мой сервер, переключит пользователя на развертывание, активирует проекты .virtualenv, что изменит каталог проекта и выдаст команду git pull.

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

Обычно я использую команду workon из virtualenvwrapper, которая является источником файла активации, а файл postactivate помещает меня в папку проекта. В этом случае кажется, что, поскольку ткань работает изнутри оболочки, управление передается ткани, поэтому я не могу использовать источник bash, встроенный в '$ source ~ / .virtualenv / myvenv / bin / activate'

У кого-нибудь есть пример и объяснение того, как они это сделали?

Томас Шрайбер
источник
1
Из любопытства, почему вы не используете workonв качестве prefix?
Дэниел С. Собрал,

Ответы:

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неправильном экранировании и наличии специальных символов оболочки и т. Д.

bitprophet
источник
7
Но workonнеизвестно sh. Как мы можем указать Fabric использовать вместо этого bash?
Pierre de LESPINAY,
18
ИМХО надо просто использовать source venv/bin/activate. Так проще и работает "из коробки". workon- это дополнительная зависимость, и даже если она установлена, вам придется добавить ее .bashrc- слишком сложно для развертывания фабрики.
Дэйв Холтер,
@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')
nh2
источник
@simon, написав свой собственный метод префикса, который вызывает .bashrc и обертывает префикс и команду в аргументе -c для bash. Смотрите ниже
Дэйв
5
Но sourceнеизвестно sh. Как мы можем указать Fabric использовать вместо этого bash?
Pierre de LESPINAY
2
@PierredeLESPINAY, вы можете использовать .вместоsource
Кэти Лавалли
Почему вы используете, cd()когда полностью указываете путь к activatein prefix()?
Nick T
@NickT Потому prefix()что, похоже, там нет компакт-диска - см. Эти документы, которые делают то же самое. Мы хотим, cdчтобы при yieldвыполнении других команд ( pip freezeв моем примере) эти команды могли относиться к этому каталогу.
nh2 05
18

Я просто использую простую функцию-оболочку virtualenv (), которую можно вызвать вместо run (). Он не использует диспетчер контекста компакт-диска, поэтому можно использовать относительные пути.

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)
EHC
источник
9

virtualenvwrapper можно сделать это немного проще

  1. Использование подхода @ nh2 (этот подход также работает при использовании local, но только для установок virtualenvwrapper, где workonнаходится $PATH, другими словами - 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помогает избежать невозможности запустить .bashrc, используя 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")
Дейв
источник
Спасибо за обобщение ответа nh2, объявление virtualenv contextmanager может быть выполнено в 5 строках на Python 2.6+, однако никогда не гарантируется, что псевдоним workon всегда импортируется правильно, и гораздо надежнее использовать `source ... / activate ' команда
Алексей Волков
8

Это мой подход к использованию virtualenv с локальными развертываниями.

Используя диспетчер контекста path (), вы можете запускать pipили pythonс двоичными файлами из 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')
darklow
источник
Мне это очень нравится - я не вижу явных минусов в этом подходе, и он очень чистый. Спасибо :)
Саймон
по-прежнему лучший и самый чистый ответ здесь
n1_
4

Спасибо за все опубликованные ответы, и я хотел бы добавить еще одну альтернативу для этого. Существует модуль fabric-virtualenv , который может предоставлять функцию в виде того же кода:

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

fabric-virtualenv fabric.context_managers.prefix, что может быть хорошим способом :)

Дрейк Гуан
источник
Интересно, но мне не нравится, что нет ссылки на SCM / трекер проблем. Пакет, который публикуется только на PYPI без ссылки на исходный код и систему отслеживания проблем, не вызывает особого доверия ... но его легко исправить.
sorin
2

Если вы хотите установить пакеты в среду или хотите запускать команды в соответствии с пакетами, которые у вас есть в среде, я нашел этот хак для решения моей проблемы вместо написания сложных методов структуры или установки новых пакетов ОС:

/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

Таким образом, вам может не понадобиться активировать среду, но вы можете выполнять команды в среде.

vikas0713
источник
1

Вот код декоратора, который приведет к использованию виртуальной среды для любых вызовов run / 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это ваш виртуальный каталог env, и добавьте этот метод, где это необходимо.

Manikanta
источник