Aktifkan virtualenv via fabric sebagai pengguna penempatan

130

Saya ingin menjalankan skrip fabric saya secara lokal, yang pada gilirannya, masuk ke server saya, beralih pengguna untuk menyebarkan, mengaktifkan proyek .virtualenv, yang akan mengubah dir ke proyek dan mengeluarkan tarikan git.

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

Saya biasanya menggunakan perintah workon dari virtualenvwrapper yang sumber file aktifkan dan file postactivate akan menempatkan saya di folder proyek. Dalam kasus ini, tampaknya karena kain beroperasi dari dalam shell, kontrol diberikan ke kain, jadi saya tidak dapat menggunakan sumber bawaan bash ke '$ source ~ / .virtualenv / myvenv / bin / activ'

Adakah yang punya contoh dan penjelasan tentang bagaimana mereka melakukan ini?

Thomas Schreiber
sumber
1
Karena penasaran, mengapa Anda tidak menggunakannya workonsebagai prefix?
Daniel C. Sobral

Jawaban:

96

Saat ini, Anda dapat melakukan apa yang saya lakukan, yang kludgy tetapi bekerja dengan sangat baik * (penggunaan ini mengasumsikan Anda menggunakan virtualenvwrapper - yang seharusnya - tetapi Anda dapat dengan mudah mengganti 'panggilan' sumber yang agak lama Anda sebutkan. , jika tidak):

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

Sejak versi 1.0, Fabric memiliki prefixmanajer konteks yang menggunakan teknik ini sehingga Anda dapat misalnya:

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

* Ada kemungkinan ada kasus di mana menggunakan command1 && command2pendekatan dapat meledak pada Anda, seperti ketika command1gagal ( command2tidak akan pernah berjalan) atau jika command1tidak lolos dengan benar dan berisi karakter shell khusus, dan sebagainya.

bitprophet
sumber
7
Namun workontidak diketahui oleh sh. Bagaimana cara kami memberi tahu fabric agar menggunakan bash?
Pierre de LESPINAY
18
IMHO, Anda hanya harus menggunakan source venv/bin/activate. Lebih mudah dan berhasil di luar kotak. workonadalah ketergantungan tambahan dan bahkan jika itu diinstal Anda harus menambahkannya .bashrc- terlalu rumit untuk penyebaran kain.
Dave Halter
@PierredeLESPINAY lihat stackoverflow.com/questions/11272372/… untuk solusi untuk masalah Anda.
duk Seseorang
137

Sebagai pembaruan untuk ramalan bitprophet: Dengan Fabric 1.0 Anda dapat menggunakan awalan () dan manajer konteks Anda sendiri.

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
sumber
@simon, dengan menulis metode awalan Anda sendiri yang memanggil .bashrc dan membungkus awalan dan perintah dalam argumen -c untuk bash. Lihat di bawah
Dave
5
Namun sourcetidak diketahui oleh sh. Bagaimana cara kami memberi tahu fabric agar menggunakan bash?
Pierre de LESPINAY
2
@PierredeLESPINAY dapat Anda gunakan .sebagai gantisource
katy lavallee
Mengapa Anda menggunakan cd()saat Anda menentukan sepenuhnya jalan activatemasuk prefix()?
Nick T
@NickT Karena prefix()sepertinya tidak cd di sana - lihat dokumen ini yang melakukan hal yang sama. Kami ingin berada di cdsana sehingga ketika kami yieldmenjalankan perintah lain ( pip freezedalam contoh saya), perintah-perintah itu dapat relatif terhadap direktori itu.
nh2
18

Saya hanya menggunakan fungsi wrapper virtualenv () yang dapat dipanggil alih-alih dijalankan (). Itu tidak menggunakan manajer konteks cd, jadi jalur relatif dapat digunakan.

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

virtualenvwrapper dapat membuat ini sedikit lebih sederhana

  1. Menggunakan pendekatan @ nh2 (pendekatan ini juga berfungsi saat menggunakan local, tetapi hanya untuk instalasi virtualenvwrapper di mana workonberada $PATH, dengan kata lain - 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. Atau gunakan file fab Anda dan jalankan ini secara lokal. Pengaturan ini memungkinkan Anda mengaktifkan virtualenv untuk perintah lokal atau jarak jauh. Pendekatan ini sangat kuat karena bekerja di sekitar localketidakmampuan untuk menjalankan .bashrc menggunakan 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
sumber
Terima kasih telah meringkas jawaban nh2, deklarasi virtualenv contextmanager dapat dilakukan dalam 5 baris pada Python 2.6+, namun tidak pernah dijamin bahwa 'workon' alias selalu diimpor dengan benar, dan jauh lebih dapat diandalkan untuk menggunakan `source ... / aktifkan ' command
Alex Volkov
8

Ini adalah pendekatan saya dalam menggunakan virtualenvdengan penyebaran lokal.

Menggunakan manajer konteks path () fabric Anda dapat menjalankan pipatau pythondengan binari dari 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
sumber
Saya sangat suka ini - saya tidak melihat ada kerugian yang jelas untuk pendekatan ini, dan itu sangat bersih. Terima kasih :)
simon
masih jawaban terbaik dan paling bersih di sini
n1_
4

Terima kasih atas semua jawaban yang diposting dan saya ingin menambahkan satu lagi alternatif untuk ini. Ada modul, fabric-virtualenv , yang dapat menyediakan fungsi sebagai kode yang sama:

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

fabric-virtualenv memanfaatkan fabric.context_managers.prefix, yang mungkin merupakan cara yang baik :)

Drake Guan
sumber
Menarik tapi saya tidak suka kenyataan bahwa tidak ada tautan ke SCM / pelacak masalah. Paket yang hanya diterbitkan di PYPI tanpa tautan ke kode sumber dan dan pelacak masalah tidak menginspirasi banyak kepercayaan .... tetapi mudah diperbaiki.
sorin
2

Jika Anda ingin menginstal paket ke lingkungan atau ingin menjalankan perintah sesuai dengan paket yang Anda miliki di lingkungan, saya telah menemukan peretasan ini untuk menyelesaikan masalah saya, alih-alih menulis metode kain yang rumit atau menginstal paket OS baru:

/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

Dengan cara ini Anda mungkin tidak perlu mengaktifkan lingkungan, tetapi Anda dapat menjalankan perintah di bawah lingkungan.

vikas0713
sumber
1

Berikut adalah kode untuk dekorator yang akan menghasilkan penggunaan Lingkungan Virtual untuk semua panggilan 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

dan kemudian menggunakan dekorator, perhatikan urutan dekorator penting:

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

Pendekatan ini berhasil bagi saya, Anda dapat menerapkan ini juga.

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

Dengan asumsi venvadalah direktori virtual env Anda dan tambahkan metode ini di mana pun sesuai.

Manikanta
sumber