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ł?


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.


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

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

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

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

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


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.


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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.