Активируйте virtualenv через fabric в качестве пользователя развертывания
-
19-09-2019 - |
Вопрос
Я хочу запустить свой скрипт fabric локально, который, в свою очередь, войдет на мой сервер, переключит пользователя на deploy, активирует projects .virtualenv, который изменит dir на project и выдаст git pull.
def git_pull():
sudo('su deploy')
# here i need to switch to the virtualenv
run('git pull')
Обычно я использую команду workon из virtualenvwrapper, которая создает файл activate, а файл postactivate помещает меня в папку проекта.В этом случае кажется, что, поскольку fabric запускается из оболочки, управление передается fabric, поэтому я не могу использовать встроенный исходный код bash для '$source ~/.virtualenv/myvenv/bin/activate'
У кого-нибудь есть пример и объяснение того, как они это сделали?
Решение
Прямо сейчас вы можете делать то, что делаю я, что является запутанным, но работает отлично * (это использование предполагает, что вы используете virtualenvwrapper - которым вы и должны быть, - но вы можете легко заменить его в упомянутом вами более длинном вызове 'source', если нет):
def task():
workon = 'workon myvenv && '
run(workon + 'git pull')
run(workon + 'do other stuff, etc')
Начиная с версии 1.0, Fabric имеет prefix
контекстный менеджер который использует эту технику, чтобы вы могли, например,:
def task():
with prefix('workon myvenv'):
run('git pull')
run('do other stuff, etc')
* Обязательно будут случаи, когда использование command1 && command2
подход может подорвать вас, например, когда command1
терпит неудачу (command2
никогда не запустится) или если command1
неправильно экранируется и содержит специальные символы оболочки и так далее.
Другие советы
В качестве обновления к прогнозу bitprophet:С Fabric 1.0 вы можете использовать префикс() и ваши собственные контекстные менеджеры.
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')
Я просто использую простую функцию-оболочку virtualenv(), которую можно вызвать вместо run().Он не использует диспетчер контекста cd, поэтому можно использовать относительные пути.
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)
virtualenvwrapper
могу сделать это немного проще
Используя подход @nh2 (этот подход также работает при использовании
local
, но только для установок virtualenvwrapper , гдеworkon
находится в$PATH
, другими словами - 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")
Или разверните свой fab-файл и запустите его локально.Эта настройка позволяет активировать virtualenv для локальных или удаленных команд.Этот подход эффективен, потому что он работает вокруг
local
невозможность запустить .bashrc с использованием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")
Это мой подход к использованию virtualenv
с локальными развертываниями.
Использование ткани путь () контекстный менеджер, который вы можете запустить pip
или python
с двоичными файлами из 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')
Спасибо за все опубликованные ответы, и я хотел бы добавить еще одну альтернативу для этого.Существует модуль, ткань-virtualenv, который может предоставлять функцию в виде того же кода:
>>> from fabvenv import virtualenv
>>> with virtualenv('/home/me/venv/'):
... run('python foo')
fabric-virtualenv использует fabric.context_managers.prefix
, что могло бы быть хорошим способом :)
Если вы хотите установить пакеты в среду или хотите запускать команды в соответствии с пакетами, которые у вас есть в среде, я нашел этот хак для решения моей проблемы, вместо написания сложных методов fabric или установки новых пакетов 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
Таким образом, вам, возможно, не потребуется активировать среду, но вы можете выполнять команды в среде.
Вот код для декоратора, который приведет к использованию виртуальной среды для любых вызовов 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
и затем, чтобы использовать декоратор, обратите внимание, что важен порядок расположения декораторов:
@task
@with_venv
def which_python():
"Gets which python is being used"
run("which python")
Этот подход сработал для меня, вы тоже можете применить его.
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'")
Предполагая venv
является вашим виртуальным каталогом env и добавляет этот метод везде, где это уместно.