Aktywuj virtualenv za pośrednictwem sieci szkieletowej jako użytkownik wdrażania

130

Chcę uruchomić mój skrypt tkaniny lokalnie, co z kolei spowoduje zalogowanie się do mojego serwera, przełączenie użytkownika na wdrożenie, aktywację projektów .virtualenv, co spowoduje zmianę katalogu na projekt i wysłanie polecenia git pull.

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

Zwykle używam polecenia workon z virtualenvwrapper, które pozyskuje plik aktywacyjny, a plik po aktywacji umieszcza mnie w folderze projektu. W tym przypadku wydaje się, że ponieważ tkanina działa z wnętrza powłoki, kontrola jest przekazywana do tkaniny, więc nie mogę użyć wbudowanego źródła basha do `` $ source ~ / .virtualenv / myvenv / bin / Activation ''

Czy ktoś ma przykład i wyjaśnienie, jak to zrobił?

Thomas Schreiber
źródło
1
Z ciekawości, dlaczego nie używasz workonjako prefix?
Daniel C. Sobral

Odpowiedzi:

96

W tej chwili możesz zrobić to, co ja, co jest niezdarne, ale działa doskonale * (to użycie zakłada, że ​​używasz virtualenvwrapper - którym powinieneś - ale możesz łatwo zastąpić w dość dłuższym wywołaniu źródła, o którym wspomniałeś , Jeśli nie):

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

Od wersji 1.0 Fabric ma prefixmenedżera kontekstu, który wykorzystuje tę technikę, dzięki czemu możesz na przykład:

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

* Z pewnością zdarzają się przypadki, w których użycie tego command1 && command2podejścia może wybuchnąć na tobie, na przykład, gdy command1zawiedzie ( command2nigdy nie zadziała) lub jeśli command1nie zostanie prawidłowo ucieczone i zawiera specjalne znaki powłoki, i tak dalej.

bitprophet
źródło
7
Ale workonjest nieznany sh. Jak możemy powiedzieć tkaninie, aby zamiast tego używała bash?
Pierre de LESPINAY
18
IMHO powinieneś po prostu użyć source venv/bin/activate. Jest to łatwiejsze i działa po wyjęciu z pudełka. workonto dodatkowa zależność, a nawet jeśli jest zainstalowana, musisz ją dodać .bashrc- zbyt skomplikowana dla wdrożeń w sieci szkieletowej.
Dave Halter
@PierredeLESPINAY zobacz stackoverflow.com/questions/11272372/ ... aby znaleźć rozwiązanie swojego problemu.
dukebody
137

Jako aktualizacja prognozy bitprophet: w Fabric 1.0 możesz skorzystać z prefix () i własnych menedżerów kontekstu.

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
źródło
@simon, pisząc własną metodę prefiksu, która wywołuje .bashrc i zawija zarówno przedrostek, jak i polecenie wewnątrz argumentu -c dla bash. Zobacz poniżej
Dave
5
Ale sourcejest nieznany sh. Jak możemy powiedzieć tkaninie, aby zamiast tego używała bash?
Pierre de LESPINAY
2
@PierredeLESPINAY, którego możesz użyć .zamiastsource
katy lavallee
Dlaczego używacie cd(), gdy jesteś całkowicie określający ścieżkę do activatew prefix()?
Nick T
@NickT Ponieważ prefix()wydaje się, że nie ma tam płyty CD - zobacz te dokumenty, które robią to samo. Chcemy cdtam, aby podczas yieldwykonywania innych poleceń ( pip freezew moim przykładzie) te polecenia były względne w stosunku do tego katalogu.
nh2
18

Po prostu używam prostej funkcji opakowującej virtualenv (), którą można wywołać zamiast run (). Nie używa menedżera kontekstu cd, więc można użyć ścieżek względnych.

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
źródło
9

virtualenvwrapper może to trochę uprościć

  1. Korzystanie z podejścia @ nh2 (podejście to działa również podczas używania local, ale tylko w przypadku instalacji virtualenvwrapper, w których workonjest $PATH, innymi słowy - 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. Lub wdróż plik fab i uruchom go lokalnie. Ta konfiguracja pozwala aktywować virtualenv dla poleceń lokalnych lub zdalnych. To podejście jest potężne, ponieważ rozwiązuje problem localbraku możliwości uruchomienia .bashrc przy użyciu 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")
    
Dave
źródło
Dzięki za podsumowanie odpowiedzi nh2, deklaracja menedżera kontekstu virtualenv może być wykonana w 5 wierszach w Pythonie 2.6+, jednak nigdy nie ma gwarancji, że alias „workon” jest zawsze poprawnie importowany, a użycie polecenia „source ... / Activation” jest znacznie bardziej niezawodne polecenie
Alex Volkov
8

To jest moje podejście do korzystania virtualenvz lokalnych wdrożeń.

Korzystając z menedżera kontekstu path () tkaniny, możesz uruchamiać piplub pythonz plikami binarnymi z 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
źródło
Bardzo mi się to podoba - nie widzę żadnych oczywistych wad tego podejścia i jest bardzo czyste. Dzięki :)
simon
wciąż najlepsza i najczystsza odpowiedź tutaj
n1_
4

Dziękuję wszystkim odpowiedziom i chciałbym dodać jeszcze jedną alternatywę. Istnieje moduł fabric-virtualenv , który może pełnić funkcję w tym samym kodzie:

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

Fabric-virtualenv wykorzystuje fabric.context_managers.prefix, co może być dobrym sposobem :)

Drake Guan
źródło
Ciekawe, ale nie podoba mi się fakt, że nie ma linku do SCM / issue trackera. Pakiet, który jest publikowany tylko w PYPI bez linku do kodu źródłowego i trackera wydań, nie budzi dużego zaufania ... ale jest łatwy do naprawienia.
sorin
2

Jeśli chcesz zainstalować pakiety w środowisku lub chcesz uruchamiać polecenia zgodnie z pakietami, które masz w środowisku, znalazłem ten hack, aby rozwiązać mój problem, zamiast pisać złożone metody tkaniny lub instalować nowe pakiety systemu operacyjnego:

/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

W ten sposób możesz nie potrzebować aktywować środowiska, ale możesz wykonywać polecenia w środowisku.

vikas0713
źródło
1

Oto kod dekoratora, który spowoduje użycie środowiska wirtualnego dla wszystkich wywołań 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

a następnie, aby użyć dekoratora, pamiętaj, że kolejność dekoratorów jest ważna:

@task
@with_venv
def which_python():
  "Gets which python is being used"
  run("which python")
Matt Campbell
źródło
1

To podejście zadziałało dla mnie, możesz to również zastosować.

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

Zakładając, że venvjest to twój wirtualny katalog env i dodaj tę metodę w razie potrzeby.

Manikanta
źródło