Attiva virtualenv tramite fabric come utente di distribuzione


130

Voglio eseguire localmente il mio script di fabric, che a sua volta accederà al mio server, passerà l'utente alla distribuzione, attiverà i progetti .virtualenv, che cambieranno dir al progetto e genereranno un git pull.

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

In genere utilizzo il comando workon di virtualenvwrapper che genera il file di attivazione e il file postactivate mi inserirà nella cartella del progetto. In questo caso, sembra che, poiché fabric viene eseguito all'interno della shell, il controllo è ceduto a fabric, quindi non posso usare il sorgente di bash incorporato in '$ source ~ / .virtualenv / myvenv / bin / activ'

Qualcuno ha un esempio e una spiegazione di come hanno fatto questo?


1
Per curiosità, perché non lo usi workoncome prefix?
Daniel C. Sobral,

Risposte:


96

In questo momento, puoi fare quello che faccio, che è kludgy ma funziona perfettamente * (questo utilizzo presuppone che tu stia utilizzando virtualenvwrapper - come dovresti essere - ma puoi facilmente sostituire la chiamata 'fonte' piuttosto più lunga che hai citato , altrimenti):

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

Dalla versione 1.0, Fabric ha un prefixgestore di contesto che utilizza questa tecnica in modo da poter ad esempio:

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

* Ci sono casi in cui l'uso command1 && command2dell'approccio può esplodere su di te, come quando command1fallisce ( command2non funzionerà mai) o se command1non è correttamente sfuggito e contiene caratteri shell speciali, e così via.


7
Ma non workonè noto sh. Come possiamo dire a Fabric di usare invece bash?
Pierre de LESPINAY,

18
IMHO dovresti semplicemente usare source venv/bin/activate. È più semplice e funziona subito. workonè una dipendenza aggiuntiva e anche se è installato è necessario aggiungerlo .bashrc- troppo complicato per le distribuzioni di fabric.
Dave Halter,

@PierredeLESPINAY vedere stackoverflow.com/questions/11272372/... di una soluzione per il vostro problema.
Dukebody,

137

Come aggiornamento alla previsione di bitprophet: con Fabric 1.0 puoi usare prefisso () e i tuoi gestori di contesto.

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, scrivendo il proprio metodo di prefisso che chiama .bashrc e racchiude sia il prefisso che il comando all'interno dell'argomento -c per bash. Vedi sotto
Dave,

5
Ma non sourceè noto sh. Come possiamo dire a Fabric di usare invece bash?
Pierre de LESPINAY,

2
@PierredeLESPINAY puoi usare .invece disource
katy lavallee il

Perché usi cd()quando specifichi completamente il percorso in activatein prefix()?
Nick T

@NickT Perché prefix()non sembra cd lì - guarda questi documenti che fanno lo stesso. Vogliamo cdlì in modo che quando eseguiamo yieldaltri comandi ( pip freezenel mio esempio), tali comandi possano essere relativi a quella directory.
nh2

18

Sto solo usando una semplice funzione wrapper virtualenv () che può essere chiamata invece di run (). Non utilizza il gestore del contesto cd, quindi è possibile utilizzare percorsi relativi.

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 può renderlo un po 'più semplice

  1. Utilizzando l'approccio di @nh2 (questo approccio funziona anche quando si utilizza local, ma solo per installazioni virtualenvwrapper in cui si workontrova $PATH, in altre parole - 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. O distribuisci il tuo file fab ed eseguilo localmente. Questa configurazione consente di attivare virtualenv per comandi locali o remoti. Questo approccio è potente perché aggira locall'impossibilità di eseguire .bashrc usando 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")

Grazie per aver riassunto la risposta di nh2, la dichiarazione del contestomanager di virtualenv potrebbe essere fatta in 5 righe su Python 2.6+, tuttavia non è mai garantito che l'alias "workon" sia sempre importato correttamente, ed è molto più affidabile usare `source ... / activ ' comando
Alex Volkov,

8

Questo è il mio approccio all'utilizzo virtualenvcon le distribuzioni locali.

Utilizzando il gestore del contesto path () di fabric è possibile eseguire pipo pythoncon i binari da 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')

Mi piace molto, non vedo alcun evidente svantaggio di questo approccio ed è molto pulito. Grazie :)
simon,

ancora la risposta migliore e più pulita qui
n1_

4

Grazie a tutte le risposte pubblicate e vorrei aggiungere un'altra alternativa per questo. Esiste un modulo, fabric-virtualenv , che può fornire la funzione con lo stesso codice:

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

fabric-virtualenv fa uso di fabric.context_managers.prefix, che potrebbe essere un buon modo :)


Interessante ma non mi piace il fatto che non ci siano collegamenti a SCM / tracker dei problemi. Un pacchetto che viene pubblicato solo su PYPI senza un collegamento al codice sorgente e che tiene traccia dei problemi non ispira molta fiducia .... ma è facile da risolvere.
sorin,

2

Se vuoi installare i pacchetti nell'ambiente o vuoi eseguire i comandi in base ai pacchetti che hai nell'ambiente, ho trovato questo trucco per risolvere il mio problema, invece di scrivere metodi complessi di fabric o installare nuovi pacchetti del sistema operativo:

/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

In questo modo potrebbe non essere necessario attivare l'ambiente, ma è possibile eseguire comandi nell'ambiente.


1

Ecco il codice per un decoratore che comporterà l'uso di Virtual Environment per qualsiasi chiamata 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

e quindi per usare il decoratore, nota che l'ordine dei decoratori è importante:

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

1

Questo approccio ha funzionato per me, è possibile applicare anche questo.

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

Supponendo che venvsia la tua directory virtuale env e aggiungi questo metodo dove appropriato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.