Активуйте virtualenv через тканину як користувач розгортання


130

Я хочу запустити свій локальний скрипт, який, у свою чергу, увійде на мій сервер, переключить користувача на розгортання, активує проекти .virtualenv, що змінить dir на проект та видасть 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не вдалося уникнути належним чином і містить спеціальні символи оболонки тощо.


7
Але workonце невідомо sh. Як ми можемо сказати тканині використовувати замість bash?
П’єр де ЛЕСПІНАЙ

18
ІМХО ви просто повинні використовувати source venv/bin/activate. Це простіше і працює з коробки. workonє додатковою залежністю, і навіть якщо вона встановлена, ви повинні додати її .bashrc- занадто складно для тканинних розгорнень.
Дейв Холтер

@PierredeLESPINAY см stackoverflow.com/questions/11272372 / ... для вирішення вашої проблеми.
герцог

137

Як оновлення прогнозу bitprophet: За допомогою Fabric 1.0 ви можете використовувати префікс () та власні менеджери контексту.

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 і загортає як префікс, так і команду в аргументі -c для bash. Дивіться нижче
Дейв

5
Але sourceце невідомо sh. Як ми можемо сказати тканині використовувати замість bash?
П’єр де ЛЕСПІНАЙ

2
@PierredeLESPINAY ви можете використовувати .замістьsource
katy lavallee

Чому ви використовуєте , cd()коли ви повністю вказати шлях до activateв prefix()?
Нік Т

@ NickT Оскільки prefix()там, схоже, немає компакт-дисків - дивіться ці документи, які роблять те саме. Ми хочемо, щоб 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, але тільки для установок 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. Або розгорніть файл файлу і запустіть його локально. Ця настройка дозволяє активувати 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')

Мені це дуже подобається - я не бачу явних недоліків у цьому підході, і він дуже чистий. Дякую :)
simon

все-таки найкраща та найчистіша відповідь тут
n1_

4

Завдяки всім опублікованим відповідям, і я хотів би додати ще одну альтернативу для цього. Є модуль, тканина-virtualenv , який може надавати функцію як один і той же код:

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

тканина-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

Таким чином, можливо, вам не потрібно буде активувати середовище, але ви можете виконувати команди в оточенні.


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 і додайте цей метод, де це доречно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.