web-dev-qa-db-fra.com

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

Je veux exécuter mon script Fabric localement, qui à son tour, se connectera à mon serveur, changera d'utilisateur pour déployer, activera les projets .virtualenv, qui changera dir pour 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 postactivate me placera dans le dossier du projet. Dans ce cas, il semble que, comme le tissu s'exécute à partir de Shell, le contrôle est confié au tissu, donc je ne peux pas utiliser la source de bash intégrée à '$ source ~/.virtualenv/myvenv/bin/activate'

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

128
Thomas Schreiber

En ce moment, vous pouvez faire ce que je fais, ce qui est maladroit 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 de 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 a un prefix gestionnaire de contexte qui utilise cette technique pour 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 du command1 && command2 l'approche peut exploser, par exemple lorsque command1 échoue (command2 ne fonctionnera jamais) ou si command1 n'est pas correctement échappé et contient des caractères Shell spéciaux, etc.

95
bitprophet

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

J'utilise simplement une fonction wrapper simple virtualenv () qui peut être appelée au lieu 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)
17
ehc

virtualenvwrapper peut rendre cela un peu plus simple

  1. Utilisation de l'approche de @ nh2 (cette approche fonctionne également lors de l'utilisation de local, mais uniquement pour les installations de virtualenvwrapper où workon est dans $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 l'incapacité de local d'exécuter .bashrc à l'aide de 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")
    
8
Dave

C'est mon approche sur l'utilisation de virtualenv avec des déploiements locaux.

En utilisant le gestionnaire de contexte path () de fabric, vous pouvez exécuter pip ou python avec 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')
7
darklow

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

4
Drake Guan

Si vous souhaitez installer les packages dans l'environnement ou 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 structure ou d'installer de nouveaux packages de système d'exploitation:

/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.

2
vikas0713

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 que venv est votre répertoire env virtuel et ajoutez cette méthode le cas échéant.

1
Manikanta

Voici le code d'un décorateur qui se traduira par 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.__= 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")
1
Matt Campbell