Activer un virtualenv via Fabric en tant qu'utilisateur de déploiement

130

Je veux exécuter mon script fabric localement, qui à son tour, se connectera à mon serveur, changera d'utilisateur à déployer, activera les projets .virtualenv, ce qui changera dir vers le projet et émettra un git pull.

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

J'utilise généralement la commande workon de virtualenvwrapper qui source le fichier d'activation et le fichier de postactivation me placera dans le dossier du projet. Dans ce cas, il semble que parce que le tissu fonctionne à partir de l'interpréteur de commandes, le contrôle est cédé au tissu, donc je ne peux pas utiliser la source intégrée de bash dans '$ source ~ / .virtualenv / myvenv / bin / activate'

Quelqu'un a-t-il un exemple et une explication de la façon dont ils ont fait cela?

Thomas Schreiber
la source
1
Par curiosité, pourquoi n'utilisez-vous pas en workontant que prefix?
Daniel C. Sobral

Réponses:

96

Pour le moment, vous pouvez faire ce que je fais, ce qui est assez compliqué mais fonctionne parfaitement bien * (cette utilisation suppose que vous utilisez virtualenvwrapper - ce que vous devriez être - mais vous pouvez facilement le remplacer par l'appel `` source '' plus long que vous avez mentionné , si non):

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

Depuis la version 1.0, Fabric dispose d'un prefixgestionnaire de contexte qui utilise cette technique afin que vous puissiez par exemple:

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

* Il y a forcément des cas où l'utilisation de l' command1 && command2approche peut exploser sur vous, par exemple en cas d' command1échec ( command2ne fonctionnera jamais) ou si elle command1n'est pas correctement échappée et contient des caractères shell spéciaux, et ainsi de suite.

bitprophet
la source
7
Mais workonest inconnu par sh. Comment pouvons-nous dire à Fabric d'utiliser bash à la place?
Pierre de LESPINAY
18
IMHO vous devez simplement utiliser source venv/bin/activate. C'est plus facile et ça marche hors de la boîte. workonest une dépendance supplémentaire et même si elle est installée, vous devez l'ajouter .bashrc- trop compliqué pour les déploiements de fabric.
Dave Halter
@PierredeLESPINAY voir stackoverflow.com/questions/11272372/… pour une solution à votre problème.
dukebody
137

En tant que mise à jour des prévisions de bitprophet: Avec Fabric 1.0, vous pouvez utiliser prefix () et vos propres gestionnaires de contexte.

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
la source
@simon, en écrivant votre propre méthode de préfixe qui appelle .bashrc et encapsule à la fois le préfixe et la commande dans l'argument -c pour bash. Voir ci
Dave
5
Mais sourceest inconnu par sh. Comment pouvons-nous dire à Fabric d'utiliser bash à la place?
Pierre de LESPINAY
2
@PierredeLESPINAY vous pouvez utiliser à la .place desource
katy lavallee
Pourquoi utilisez-vous cd()lorsque vous spécifiez complètement le chemin d'accès à activatein prefix()?
Nick T
@NickT Parce que prefix()cela ne semble pas y aller - voir ces documents qui font de même. Nous voulons cdlà pour que lorsque nous yieldexécutons d'autres commandes ( pip freezedans mon exemple), ces commandes puissent être relatives à ce répertoire.
nh2
18

J'utilise juste une simple fonction wrapper virtualenv () qui peut être appelée à la place de run (). Il n'utilise pas le gestionnaire de contexte cd, donc des chemins relatifs peuvent être utilisés.

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)
hein
la source
9

virtualenvwrapper peut rendre cela un peu plus simple

  1. Utilisation de l'approche de @ nh2 (cette approche fonctionne également lors de l'utilisation local, mais uniquement pour les installations de virtualenvwrapper où se workontrouve $PATH, en d'autres termes - 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. Ou déployez votre fichier fab et exécutez-le localement. Cette configuration vous permet d'activer virtualenv pour les commandes locales ou distantes. Cette approche est puissante car elle contourne locall'incapacité de exécuter .bashrc en utilisant 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
la source
Merci d'avoir résumé la réponse de nh2, la déclaration de virtualenv contextmanager pourrait être effectuée en 5 lignes sur Python 2.6+, mais il n'est jamais garanti que l'alias 'workon' est toujours importé correctement, et il est beaucoup plus fiable d'utiliser `source ... / activate ' commande
Alex Volkov
8

C'est mon approche d'utilisation virtualenvavec les déploiements locaux.

En utilisant le gestionnaire de contexte de fabric path () , vous pouvez exécuter pipou pythonavec des binaires 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')
darklow
la source
J'aime beaucoup cela - je ne vois pas d'inconvénients évidents à cette approche, et c'est très propre. Merci :)
simon
toujours la meilleure et la plus claire réponse ici
n1_
4

Merci à toutes les réponses publiées et je voudrais ajouter une autre alternative pour cela. Il existe un module, fabric-virtualenv , qui peut fournir la fonction sous le même code:

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

fabric-virtualenv utilise fabric.context_managers.prefix, ce qui pourrait être un bon moyen :)

Drake Guan
la source
Intéressant mais je n'aime pas le fait qu'il n'y ait pas de lien vers SCM / issue tracker. Un package qui n'est publié que sur PYPI sans lien vers le code source et sans suivi des problèmes n'inspire pas beaucoup de confiance ... mais est facile à corriger.
sorin
2

Si vous souhaitez installer les packages dans l'environnement ou si vous souhaitez exécuter des commandes en fonction des packages que vous avez dans l'environnement, j'ai trouvé ce hack pour résoudre mon problème, au lieu d'écrire des méthodes complexes de fabric ou d'installer de nouveaux packages OS:

/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 cette façon, vous n'aurez peut-être pas besoin d'activer l'environnement, mais vous pouvez exécuter des commandes sous l'environnement.

vikas0713
la source
1

Voici le code d'un décorateur qui entraînera l'utilisation de l'environnement virtuel pour tous les appels 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

puis pour utiliser le décorateur, notez que l'ordre des décorateurs est important:

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

Cette approche a fonctionné pour moi, vous pouvez également l'appliquer.

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

En supposant qu'il venvs'agit de votre répertoire d'environnement virtuel et ajoutez cette méthode le cas échéant.

Manikanta
la source