Jenkinsfile и Python virtualenv

Я пытаюсь настроить проект, в котором используются блестящие новые конвейеры Jenkins, а более конкретно - проект с несколькими каналами.

У меня есть Jenkinsfile, созданный в тестовой ветке, как показано ниже:

node {
    stage 'Preparing VirtualEnv'
    if (!fileExists('.env')){
        echo 'Creating virtualenv ...'
        sh 'virtualenv --no-site-packages .env'
    }
    sh '. .env/bin/activate'
    sh 'ls -all'
    if (fileExists('requirements/preinstall.txt')){
        sh 'pip install -r requirements/preinstall.txt'
    }
    sh 'pip install -r requirements/test.txt'
    stage 'Unittests'
    sh './manage.py test --noinput'
}

Стоит отметить, что preinstall.txt обновит пип

Я получаю ошибку, как показано ниже:

OSError: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/pip'

Похоже, он пытается обновить pip в глобальном env, а не внутри virtualenv, и выглядит так, как каждый sh шаг находится в своем собственном контексте, как заставить их выполнять в том же контексте?

Ответ 1

То, что вы пытаетесь сделать, не сработает. Каждый раз, когда вы вызываете команду sh, jenkins создаст новую оболочку.

Это означает, что если вы используете .env/bin/activate в sh это будут только источники в этом сеансе оболочки. В результате в новой команде sh вам нужно снова запустить файл (если вы более подробно рассмотрите вывод консоли, вы увидите, что Jenkins на самом деле будет создавать временные файлы оболочки каждый раз, когда вы запустите запятую.

Поэтому вы должны либо .env/bin/activate файл .env/bin/activate в начале каждой команды оболочки (вы можете использовать тройные qoutes для многострочных строк), например

if (fileExists('requirements/preinstall.txt')) {
    sh """
    . .env/bin/activate
    pip install -r requirements/preinstall.txt
    """
}
...
sh """
. .env/bin/activate
pip install -r requirements/test.txt
"""
}
stage("Unittests") {
    sh """
    . .env/bin/activate
    ./manage.py test --noinput
    """
}

или запустить все в одной оболочке

sh """
. .env/bin/activate
if [[ -f requirements/preinstall.txt ]]; then
    pip install -r requirements/preinstall.txt
fi
pip install -r requirements/test.txt
./manage.py test --noinput
"""

Ответ 2

Как отмечал Rik, virtualenvs плохо работают в среде Jenkins Pipeline Environment, поскольку для каждой команды создается новая оболочка.

Я создал плагин, который делает этот процесс немного менее болезненным, который можно найти здесь: https://wiki.jenkins.io/display/JENKINS/Pyenv+Pipeline+Plugin. Он по сути просто обертывает каждый вызов таким образом, который активирует virtualenv до запуска команды. Это само по себе сложно, так как некоторые методы запуска нескольких команд inline разделены на две отдельные команды Jenkins, в результате чего активированный virtualenv больше не применяется.

Ответ 3

Я новичок в файлах Jenkins. Вот как я работал над проблемой виртуальной среды. (Я запускаю Python3, Jenkins 2.73.1)

Предостережение. Чтобы быть ясным, я не говорю, что это хороший способ решить проблему, и я не проверял это достаточно, чтобы стоять за этим подходом, но вот что для меня сегодня работает:

Я играю вокруг, минуя "активировать" venv, вызывая напрямую интерпретатор python виртуальной среды. Поэтому вместо:

source ~/venv/bin/activate    

можно использовать:

~/venv/bin/python3 my_script.py

Я передаю путь к интерпретатору python виртуальной среды через файл оболочки rc (в моем случае, ~/.bashrc.) Теоретически каждый вызов Jenkins оболочки должен читать этот файл ресурсов. На практике я должен перезапустить Jenkins после внесения изменений в файл ресурсов оболочки.

HOME_DIR=~
export VENV_PATH="$HOME_DIR/venvs/my_venv"
export PYTHON_INTERPRETER="${VENV_PATH}/bin/python3"

Мой Jenkinsfile выглядит примерно так:

pipeline {
    agent {
        label 'my_slave'
    }

    stages {
        stage('Stage1') {
            steps {
                // sh 'echo $PYTHON_INTERPRETER'
                // sh 'env | sort'
                sh "$PYTHON_INTERPRETER my_script.py "
            }
        }
    }
}

Поэтому при запуске конвейера sh имеет установленные значения среды $ PYTHON_INTERPRETER.

Обратите внимание, что одним из недостатков этого подхода является то, что теперь файл Jenkins не содержит всю необходимую информацию для правильного запуска скрипта. Надеюсь, это уберет вас от земли.