Active un virtualenv a través de fabric como usuario de implementación

130

Quiero ejecutar mi script de tela localmente, que a su vez, iniciará sesión en mi servidor, cambiará el usuario para implementar, activará los proyectos .virtualenv, lo que cambiará el directorio del proyecto y emitirá un git pull.

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

Normalmente uso el comando workon de virtualenvwrapper que obtiene el archivo de activación y el archivo de postactivación me colocará en la carpeta del proyecto. En este caso, parece que debido a que el tejido se ejecuta desde el shell, el control se transfiere al tejido, por lo que no puedo usar la fuente de bash incorporada a '$ source ~ / .virtualenv / myvenv / bin / enable'

¿Alguien tiene un ejemplo y una explicación de cómo han hecho esto?

Thomas Schreiber
fuente
1
Por curiosidad, ¿por qué no lo estás usando workoncomo un prefix?
Daniel C. Sobral

Respuestas:

96

En este momento, puedes hacer lo que yo hago, que es grosero pero funciona perfectamente bien * (este uso supone que estás usando virtualenvwrapper, que deberías serlo), pero puedes sustituirlo fácilmente en la llamada 'fuente' más larga que mencionaste , si no):

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

Desde la versión 1.0, Fabric tiene un prefixadministrador de contexto que utiliza esta técnica para que pueda, por ejemplo:

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

* Es command1 && command2probable que haya casos en los que el uso del enfoque pueda explotar sobre usted, como cuando command1falla ( command2nunca se ejecutará) o si command1no se escapa correctamente y contiene caracteres especiales de shell, y así sucesivamente.

bitprophet
fuente
77
Pero workones desconocido por sh. ¿Cómo podemos decirle a la tela que use bash en su lugar?
Pierre de LESPINAY
18
En mi humilde opinión, solo debes usar source venv/bin/activate. Es más fácil y funciona fuera de la caja. workones una dependencia adicional e incluso si está instalado, debe agregarlo .bashrc, demasiado complicado para las implementaciones de fabric.
Dave Halter
@PierredeLESPINAY, consulte stackoverflow.com/questions/11272372/… para encontrar una solución a su problema.
dukebody
137

Como una actualización del pronóstico de bitprophet: con Fabric 1.0 puede utilizar el prefijo () y sus propios administradores de contexto.

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
fuente
@simon, escribiendo su propio método de prefijo que llama a .bashrc y ajusta el prefijo y el comando dentro del argumento -c para bash. Vea a continuación
Dave, el
55
Pero sourcees desconocido por sh. ¿Cómo podemos decirle a la tela que use bash en su lugar?
Pierre de LESPINAY
2
@PierredeLESPINAY que puedes usar en .lugar desource
katy lavallee
¿Por qué lo usas cd()cuando estás especificando completamente la ruta hacia activateadentro prefix()?
Nick T
@NickT Debido a prefix()que no parece cd allí, vea estos documentos que hacen lo mismo. Queremos cdallí para que cuando yieldejecutemos otros comandos ( pip freezeen mi ejemplo), esos comandos pueden ser relativos a ese directorio.
nh2
18

Solo estoy usando una función de contenedor simple virtualenv () que se puede llamar en lugar de run (). No usa el administrador de contexto de CD, por lo que se pueden usar rutas relativas.

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
fuente
9

virtualenvwrapper puede hacer esto un poco más simple

  1. Uso del enfoque de @ nh2 (este enfoque también funciona cuando se usa local, pero solo para instalaciones virtualenvwrapper donde se workonencuentra $PATH, en otras palabras, 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 implemente su archivo fab y ejecútelo localmente. Esta configuración le permite activar virtualenv para comandos locales o remotos. Este enfoque es poderoso porque localevita la incapacidad de ejecutar .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")
Dave
fuente
Gracias por resumir la respuesta de nh2, la declaración virtualenv contextmanager podría hacerse en 5 líneas en Python 2.6+, sin embargo, nunca se garantiza que el alias 'workon' siempre se importe correctamente, y es mucho más confiable usar 'source ... / enable' comando
Alex Volkov
8

Este es mi enfoque sobre el uso virtualenvcon implementaciones locales.

Usando el administrador de contexto de path () de fabric puede ejecutar pipo pythoncon binarios de 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')
oscuro
fuente
Esto me gusta mucho: no veo ninguna desventaja obvia en este enfoque, y está muy limpio. Gracias :)
Simon
sigue siendo la mejor y más limpia respuesta aquí
n1_
4

Gracias a todas las respuestas publicadas y me gustaría agregar una alternativa más para esto. Hay un módulo, fabric-virtualenv , que puede proporcionar la función como el mismo código:

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

fabric-virtualenv hace uso de fabric.context_managers.prefix, lo que podría ser una buena manera :)

Drake Guan
fuente
Interesante pero no me gusta el hecho de que no hay un enlace a SCM / tracker de problemas. Un paquete que solo se publica en PYPI sin un enlace al código fuente y un rastreador de problemas no inspira mucha confianza ... pero es fácil de solucionar.
sorin
2

Si desea instalar los paquetes en el entorno o desea ejecutar comandos de acuerdo con los paquetes que tiene en el entorno, he encontrado este truco para resolver mi problema, en lugar de escribir métodos complejos de tela o instalar nuevos paquetes de 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

De esta manera, es posible que no necesite activar el entorno, pero puede ejecutar comandos en el entorno.

vikas0713
fuente
1

Aquí hay un código para un decorador que dará como resultado el uso del entorno virtual para cualquier llamada de ejecución / 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

y luego para usar el decorador, tenga en cuenta que el orden de los decoradores es importante:

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

Este enfoque funcionó para mí, puedes aplicar esto también.

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

Asumiendo que venves su directorio virtual env y agregue este método cuando sea apropiado.

Manikanta
fuente