解决fabric执行远程命令时无法加载pyenv的python环境

在用fabric run()的时候,发现以下代码报错:

@task
def migrate():
    """
    -- perform migrations
    """
    with cd('~/project/proj1'):
        run('python manage.py migrate')

报错如下:

Executing task 'migrate'
run: python manage.py migrate
out: Traceback (most recent call last):
out:   File "manage.py", line 17, in <module>
out:     "Couldn't import Django. Are you sure it's installed and "
out: ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?

看提示是无法导入django。我的django项目是运行在pyenv建立的virtualenv中的,手动ssh上去执行python manage.py migrate是正常的,怎么用fabric就加载不了虚拟环境了呢?

再排查一下服务器上的~/.bashrc,加载pyenv的代码块也是放得好好的:

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

没辙了,先试试手动source一下~/.bashrc

@task
def migrate():
    """
    -- perform migrations
    """
    with cd('~/project/proj1'):
        run('source ~/.bashrc && python manage.py migrate')

问题依旧。

看样子是~/.bashrc中的pyenv代码块根本没起作用,或者……根本没执行到?
尝试一下把pyenv代码块移到~/.profile中,再试一次fab migrate,居然可以了!

于是继续爬文,发现了线索:
bash -c command是以非交互模式执行shell,而fabric正是以这种方式来执行run()的
非交互模式下,~/.bashrc是不会被执行的,因为开头有这样的代码:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

至此问题水落石出,难怪放在~/.bashrc末尾的pyenv初始化代码块没有被执行了。
解决方案也正好就是上面提到的,把pyenv代码块挪到~/.profile(或者~/.bash_profile,不同OS不尽相同)中。

最后补充个关于login shell和interactive shell的知识,貌似比较绕,有空要好好琢磨一下:
https://wido.me/sunteya/understand-bashrc-and-profile

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据