Ative uma virtualenv via fabric como usuário de implantação

130

Desejo executar meu script de malha localmente, o que, por sua vez, efetua login no servidor, alterna o usuário para implantar, ativa os projetos .virtualenv, que altera o diretório do projeto e emite um git pull.

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

Normalmente, uso o comando workon do virtualenvwrapper, que origina o arquivo de ativação e o arquivo de pós-atividade me colocará na pasta do projeto. Nesse caso, parece que, como a malha é executada dentro do shell, o controle é transferido para a malha, então não posso usar a fonte do bash incorporada para '$ source ~ / .virtualenv / myvenv / bin / enable'

Alguém tem um exemplo e explicação de como eles fizeram isso?

Thomas Schreiber
fonte
1
Por curiosidade, por que você não está usando workoncomo prefix?
Daniel C. Sobral

Respostas:

96

No momento, você pode fazer o que eu faço, o que é desleixado, mas funciona perfeitamente bem * (esse uso pressupõe que você esteja usando o virtualenvwrapper - o que deveria ser - mas você pode substituir facilmente na chamada de "origem" que você mencionou , se não):

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

Desde a versão 1.0, o Fabric possui um prefixgerenciador de contexto que usa essa técnica para que você possa, por exemplo:

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

* É provável que existam casos em que o uso da command1 && command2abordagem pode explodir em você, como quando command1falha ( command2nunca será executada) ou se command1não for escapado adequadamente e contiver caracteres especiais do shell e assim por diante.

bitprophet
fonte
7
Mas workoné desconhecido por sh. Como podemos dizer ao tecido para usar o bash?
Pierre de LESPINAY
18
IMHO você deve apenas usar source venv/bin/activate. É mais fácil e funciona imediatamente. workoné uma dependência adicional e, mesmo que esteja instalado, é necessário adicioná-lo .bashrc- muito complicado para implantações de malha.
precisa saber é o seguinte
@PierredeLESPINAY, consulte stackoverflow.com/questions/11272372/… para obter uma solução para o seu problema.
dukebody
137

Como uma atualização da previsão do bitprophet: Com o Fabric 1.0, você pode usar o prefix () e seus próprios gerenciadores 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
fonte
@simon, escrevendo seu próprio método de prefixo que chama .bashrc e agrupa o prefixo e o comando no argumento -c para bash. Veja abaixo
Dave
5
Mas sourceé desconhecido por sh. Como podemos dizer ao tecido para usar o bash?
Pierre de LESPINAY
2
@PierredeLESPINAY você pode usar em .vez desource
katy lavallee 30/11
Por que você usa cd()quando você está especificando totalmente o caminho para activatenos prefix()?
Nick T
@ NickT Porque prefix()não parece cd lá - veja esses documentos que fazem o mesmo. Queremos cdlá para que, quando yieldexecutarmos outros comandos ( pip freezeno meu exemplo), esses comandos possam ser relativos a esse diretório.
nh2 5/12/13
18

Estou apenas usando uma função simples do invólucro virtualenv () que pode ser chamada em vez de run (). Ele não usa o gerenciador de contexto do cd, portanto, os caminhos relativos podem ser usados.

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

virtualenvwrapper pode tornar isso um pouco mais simples

  1. Usando a abordagem do @ nh2 (essa abordagem também funciona ao usar local, mas apenas para instalações do virtualenvwrapper em que workonestá $PATH, em outras palavras - 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 implemente seu arquivo fab e execute-o localmente. Essa configuração permite ativar o virtualenv para comandos locais ou remotos. Essa abordagem é poderosa porque funciona em torno localda incapacidade de executar .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
fonte
Obrigado por resumir a resposta do nh2, a declaração virtualenv contextmanager pode ser feita em 5 linhas no Python 2.6+, no entanto, nunca é garantido que o alias 'workon' seja sempre importado corretamente e é muito mais confiável usar o `source ... / enable ' comando
Alex Volkov
8

Esta é minha abordagem ao usar virtualenvcom implantações locais.

Usando o gerenciador de contexto path () da malha, você pode executar pipou pythoncom binários do 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
fonte
Eu gosto muito disso - não vejo nenhuma desvantagem óbvia nessa abordagem e é muito limpa. Obrigado :)
simon
ainda a resposta melhor e mais limpa aqui
n1_
4

Obrigado a todas as respostas postadas e gostaria de adicionar mais uma alternativa para isso. Há um módulo, fabric-virtualenv , que pode fornecer a função como o mesmo código:

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

fabric-virtualenv faz uso fabric.context_managers.prefix, o que pode ser uma boa maneira :)

Drake Guan
fonte
Interessante, mas não gosto do fato de não haver um link para o SCM / rastreador de problemas. Um pacote publicado apenas no PYPI sem um link para o código-fonte e o rastreador de problemas não inspira muita confiança ... mas é fácil de corrigir.
sorin 14/05
2

Se você deseja instalar os pacotes no ambiente ou executar comandos de acordo com os pacotes existentes no ambiente, encontrei esse truque para resolver meu problema, em vez de escrever métodos complexos de fabric ou instalar novos pacotes do sistema operacional:

/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

Dessa forma, talvez você não precise ativar o ambiente, mas pode executar comandos no ambiente.

vikas0713
fonte
1

Aqui está o código para um decorador que resultará no uso do Ambiente Virtual para qualquer chamada de execução / 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, em seguida, para usar o decorador, observe que a ordem dos decoradores é importante:

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

Essa abordagem funcionou para mim, você pode aplicar isso também.

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

Supondo que venvseja seu diretório virtual de env e adicione esse método sempre que apropriado.

Manikanta
fonte