Переменные среды, withEnv и рабочие пространства

Рейтинг: 59.6% · 10 голосов
Подробный курс по Jenkins и CI/CD: установка и настройка, плагины и Configuration as Code, конвейеры (pipeline) и Jenkinsfile, declarative и scripted, агенты, Docker и Kubernetes, общие библиотеки на Groovy, безопасность и credentials, интеграции (SonarQube, артефакты), траблшутинг. Актуально на 2026.
Ответить
Аватара пользователя
Andrey_CICD
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Переменные среды, withEnv и рабочие пространства

Сообщение Andrey_CICD »

Оглавление курса (47)
  1. Что такое Jenkins и зачем нужен CI/CD
  2. Jenkins в 2026: версии, экосистема и сравнение с GitLab CI и GitHub Actions
  3. Архитектура Jenkins: контроллер, агенты, узлы и исполнители
  4. Установка Jenkins: пакеты, Docker и Kubernetes
  5. Первичная настройка Jenkins: мастер, плагины, пользователи
  6. Плагины Jenkins: установка, обновление и ключевой набор
  7. Configuration as Code (JCasC): настройка Jenkins декларативно
  8. Конвейеры Jenkins и Jenkinsfile: pipeline as code
  9. Scripted против Declarative: два синтаксиса конвейера
  10. Структура конвейера: node, stage, steps и первый pipeline
  11. Генератор сниппетов, запуск конвейера и Replay
  12. Типы заданий Jenkins: Freestyle, Pipeline, Multibranch и Organization
  13. Декларативный конвейер: структура pipeline, agent, stages
  14. agent, environment и переменные в конвейере
  15. tools, options, parameters и triggers в декларативном конвейере
  16. stages, parallel и matrix: организация этапов
  17. Условное выполнение when и блок post
  18. Триггеры сборки: webhook от GitHub, SCM polling и расписание
  19. Параметры сборки и пользовательский ввод
  20. Управление потоком: timeout, retry, sleep и waitUntil
  21. Параллелизм и блокировки: parallel, lock, milestone
  22. Постобработка и уведомления о результате сборки
  23. Groovy для Jenkins: основы скриптового конвейера
  24. Скриптовый конвейер: логика, циклы и функции
  25. Общие библиотеки Jenkins: Shared Libraries
  26. Подключение и загрузка общих библиотек
  27. Шаги оболочки: sh, bat, powershell и коды возврата
  28. Переменные среды, withEnv и рабочие пространства (вы здесь)
  29. Работа с файлами, артефактами и отпечатками
  30. Сборка проектов в конвейере: Maven, Gradle, npm
  31. Качество кода: SonarQube и покрытие тестами
  32. Управление артефактами: публикация в Nexus и Artifactory
  33. Docker в Jenkins: образы как агенты сборки
  34. Сборка и публикация Docker-образов из конвейера
  35. Jenkins в Kubernetes: динамические pod-агенты
  36. Развёртывание в Kubernetes из конвейера
  37. Безопасность Jenkins: аутентификация и матрица прав
  38. Учётные данные Jenkins: credentials в конвейере
  39. Безопасность сценариев: Groovy sandbox и Vault
  40. Уведомления и отчёты: email, Slack, Telegram, HTML
  41. Blue Ocean и интерфейс Jenkins в 2026
  42. Автоматизация Jenkins: CLI, REST API и script console
  43. Поиск и устранение неисправностей: читаем логи конвейера
  44. Типичные ошибки конвейера: сериализация и неутверждённый код
  45. Эксплуатация Jenkins: бэкап, обновление, масштабирование
  46. Конвертация и миграция: от Freestyle к Jenkinsfile
  47. Итог: сквозной CI/CD конвейер и Jenkins против альтернатив в 2026
Каждый раз, когда сборка где-то спотыкается, расследование начинается с двух вопросов: какие переменные были выставлены и в каком каталоге всё запускалось. Ты пишешь

Код: Выделить всё

sh 'export TAG=1.2.3'
в одном шаге, а в следующем переменная пустая. Или находишь в логе чужой артефакт, который остался от прошлой сборки и сломал текущую. Или билд падает с "file not found", хотя файл точно есть - просто не там, где Jenkins его ищет. Всё это - про окружение и рабочее пространство. Разберёмся, как Jenkins выставляет переменные среды, как добавить свои на весь конвейер или локально на один блок, и почему workspace надо держать в чистоте.

Встроенные jenkins переменные среды: что доступно из коробки

Перед запуском любого шага Jenkins сам наполняет окружение служебными значениями. Это те самые jenkins env, которые доступны в каждом конвейере без всякой настройки. Самые ходовые:
  • BUILD_NUMBER - порядковый номер текущей сборки, удобно для тегов образов и версий артефактов.
  • JOB_NAME - имя джобы (с папками через слеш, если есть).
  • WORKSPACE - абсолютный путь к рабочему каталогу на агенте, где развёрнут код.
  • BUILD_URL и BUILD_ID - ссылка на сборку и её идентификатор.
  • NODE_NAME - имя агента, на котором всё крутится.
Если в пайплайне есть выгрузка из Git (плагин Git добавляет свои значения), появятся GIT_BRANCH, GIT_COMMIT, GIT_URL. Учти важный нюанс: GIT_BRANCH заполняется не всегда. В multibranch-пайплайне ветку правильнее брать из BRANCH_NAME, а GIT_* появляются только после фактического

Код: Выделить всё

checkout scm
или шага git. Полный список того, что доступно именно на твоём контроллере, всегда лежит по адресу твой-jenkins/env-vars.html - это живая страница, она отражает установленные плагины.

В Declarative-конвейере обращайся к переменным через :

Код: Выделить всё

pipeline {
    agent any
    stages {
        stage('Info') {
            steps {
                echo "Сборка #${env.BUILD_NUMBER} джобы ${env.JOB_NAME}"
                echo "Код развёрнут в ${env.WORKSPACE}"
                sh 'echo "Внутри shell: $BUILD_NUMBER на ветке $GIT_BRANCH"'
            }
        }
    }
}
Обрати внимание на разницу: в Groovy-строках интерполяция идёт через

Код: Выделить всё

${env.BUILD_NUMBER}
, а внутри переменная уже экспортирована в шелл, поэтому там работает обычный

Код: Выделить всё

$BUILD_NUMBER
. Не смешивай: если написать

Код: Выделить всё

sh "echo ${env.BUILD_NUMBER}"
в двойных кавычках, подстановку сделает Groovy ещё до запуска шелла - это нормально, но секреты так светить нельзя, они попадут в лог. Для чувствительных значений всегда одинарные кавычки и шелл-интерполяция.

Изображение

Свои переменные: jenkins environment против jenkins withenv

Своё окружение задаётся двумя способами, и важно понимать, когда какой.

Блок environment - декларативный. Объявляешь переменные на уровне всего pipeline или конкретного stage, и они живут весь конвейер (или весь stage). Это статика, выбор по умолчанию для значений, которые известны заранее:

Код: Выделить всё

pipeline {
    agent any
    environment {
        APP_NAME = 'cyberlake-api'
        IMAGE    = "registry.local/${env.JOB_NAME}:${env.BUILD_NUMBER}"
        // секрет из Credentials - подставится как переменная, маскируется в логе
        REGISTRY_TOKEN = credentials('registry-token')
    }
    stages {
        stage('Build') {
            steps {
                sh 'echo "Собираю $APP_NAME, тег $IMAGE"'
            }
        }
        stage('Test') {
            environment {
                // переопределение только для этого stage
                APP_ENV = 'testing'
            }
            steps {
                sh 'echo "Тесты в окружении $APP_ENV для $APP_NAME"'
            }
        }
    }
}
Отдельно про

Код: Выделить всё

credentials('id')
в блоке environment: для secret text получишь одну переменную, а для пары логин/пароль Jenkins создаст три - саму переменную плюс _USR и _PSW. Удобно для docker login и подобного.

Шаг withEnv - это про динамику и локальность. jenkins withenv задаёт переменные только внутри своего блока, а как только блок закрылся, они исчезают. Незаменимо, когда значение вычисляется на лету - например, тег из git-хеша, который ты получил прямо в пайплайне:

Код: Выделить всё

pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                script {
                    def shortSha = sh(script: 'git rev-parse --short HEAD',
                                      returnStdout: true).trim()
                    withEnv(["IMAGE_TAG=${shortSha}", "DEPLOY_ENV=staging"]) {
                        sh 'echo "Деплою тег $IMAGE_TAG в $DEPLOY_ENV"'
                        sh 'docker build -t app:$IMAGE_TAG .'
                    }
                    // здесь IMAGE_TAG уже не существует
                }
            }
        }
    }
}
Особый приём - дополнение PATH вместо перезаписи. Jenkins понимает суффикс через плюс: имя

Код: Выделить всё

PATH+ANYTHING
не заменяет PATH, а дописывается в начало. Так подключают локальный тулчейн, не ломая системный путь:

Код: Выделить всё

withEnv(["PATH+NODE=${env.WORKSPACE}/node_modules/.bin"]) {
    sh 'eslint --version'
}
Часть после плюса (NODE) - произвольная метка, она ни на что не влияет, важен сам префикс PATH. В scripted-конвейере withEnv работает один в один так же - это базовый шаг, доступный везде.

Рабочее пространство: jenkins workspace и изоляция сборок

jenkins workspace - это каталог на агенте, куда выгружается код и где выполняются шаги. По умолчанию путь привязан к джобе: для FreeStyle обычно JENKINS_HOME/workspace/имя-джобы, для пайплайнов агент сам выбирает каталог и кладёт его в

Код: Выделить всё

env.WORKSPACE
. Ключевой принцип - изоляция: у каждой джобы своё пространство, чтобы сборки не топтались по чужим файлам.

Но изоляция не абсолютная. Если одна и та же джоба запустилась дважды параллельно на одном агенте, Jenkins создаст второй каталог с суффиксом , третий - и так далее. Это надо помнить, когда вручную лезешь в файлы соседней сборки.

Сменить каталог на время помогает шаг dir. Он меняет текущую директорию для вложенных шагов и сам возвращает всё назад на выходе:

Код: Выделить всё

dir('build') {
    sh 'cmake .. && make'   // выполняется внутри WORKSPACE/build
}
sh 'pwd'                    // снова в корне workspace
Если нужен каталог вообще вне стандартного workspace джобы - например, общий кеш зависимостей между сборками, - есть шаг ws. Он переключает выполнение в указанное (или новое выделенное) рабочее пространство:

Код: Выделить всё

ws("${env.WORKSPACE}/../shared-cache") {
    sh 'ls -la'
}
Пользуйся ws аккуратно: общий каталог между джобами рушит ту самую изоляцию, ради которой workspace и придуман. Для кешей чаще берут отдельный шаг плагина или внешнее хранилище, но знать про ws полезно.

Очистка workspace: cleanWs и deleteDir

Грязное рабочее пространство - источник плавающих багов. Остался node_modules от прошлой версии, лежит старый артефакт, недоудалённый файл сборки - и ты ловишь "у меня собирается, а на CI нет" в обратную сторону. Два инструмента наведения порядка:
  • deleteDir - базовый шаг, рекурсивно удаляет текущий каталог со всем содержимым. Чтобы снести конкретную папку, оберни в dir.
  • cleanWs - из плагина Workspace Cleanup, умнее: умеет чистить по паттернам, по статусу сборки, не валит билд при ошибке удаления.

Код: Выделить всё

pipeline {
    agent any
    stages {
        stage('Checkout') {
            steps {
                deleteDir()        // чистим перед стартом
                checkout scm
            }
        }
        stage('Build') {
            steps { sh 'make' }
        }
    }
    post {
        always {
            // удалить всё, кроме логов, только при успехе - оставить артефакты при падении
            cleanWs(cleanWhenFailure: false,
                    deleteDirs: true,
                    patterns: [[pattern: 'logs/**', type: 'EXCLUDE']])
        }
        cleanup {
            // финальный безусловный проход
            cleanWs()
        }
    }
}
Полезная привычка: чистить и в начале (свежий старт), и в конце (не оставлять мусор и не раздувать диск агента). На эфемерных Kubernetes-агентах эта проблема снимается сама собой - под удаляется вместе с workspace после сборки, и каждая сборка стартует на чистом месте. Это, кстати, одна из причин, почему на современных стендах 2026 года предпочитают одноразовые pod-агенты: изоляция и чистота получаются бесплатно, без cleanWs.

Типичные грабли
  • Переменная не видна между sh-шагами. Самая частая боль.

    Код: Выделить всё

    sh 'export TAG=v1'
    и следом

    Код: Выделить всё

    sh 'echo $TAG'
    - второй шаг пустой. Каждый sh - это отдельный процесс шелла, export живёт только внутри него. Решение: либо один

    Код: Выделить всё

    sh '''...'''
    на несколько команд, либо вытащить значение через

    Код: Выделить всё

    returnStdout: true
    в Groovy-переменную и прокинуть через env/withEnv.
  • Присваивание env внутри script не везде долетает.

    Код: Выделить всё

    env.FOO = 'bar'
    работает в scripted и в блоке script, но логичнее статику класть в environment, а динамику - в withEnv.
  • Маскирование секретов ломается интерполяцией.

    Код: Выделить всё

    sh "curl -H 'token: ${TOKEN}'"
    подставит секрет ещё в Groovy, и в логе он засветится. Одинарные кавычки и - секрет подставит шелл, Jenkins его замаскирует.
  • Параллельные сборки и каталог @2. Если хардкодишь путь вместо

    Код: Выделить всё

    env.WORKSPACE
    , при параллельном запуске два билда дерутся за один каталог. Всегда от env.WORKSPACE.
  • cleanWs без плагина. Шаг не существует, пока не установлен Workspace Cleanup. Если его нет - либо ставь плагин, либо обходись deleteDir.
Мини-лаба

Сделай руками, чтобы уложилось:
  • Собери pipeline, который печатает BUILD_NUMBER, JOB_NAME и WORKSPACE через echo и через sh - сравни, как интерполяция работает в Groovy и в шелле.
  • Заведи переменную в блоке environment на уровне pipeline и переопредели её в одном stage. Убедись по логу, что значение в этом stage другое.
  • Воспроизведи грабли с export: два sh-шага, в первом export, во втором echo. Увидь пустоту. Почини, собрав обе команды в один

    Код: Выделить всё

    sh '''...'''
    .
  • В блоке withEnv вычисли тег из

    Код: Выделить всё

    git rev-parse --short HEAD
    и используй его в sh. После блока попробуй вывести переменную снова - убедись, что она исчезла.
  • Добавь

    Код: Выделить всё

    deleteDir()
    перед checkout и

    Код: Выделить всё

    cleanWs()
    в post.cleanup. Запусти дважды и проверь, что второй прогон стартует на чистом месте.
Контрольные вопросы
  • Почему переменная, заданная через export в одном sh-шаге, не видна в следующем sh-шаге, и как это обойти?
  • В чём разница между блоком environment и шагом withEnv - когда выбирать каждый?
  • Что делает суффикс PATH+ЧТО-ТО и зачем он нужен?
  • Чем deleteDir отличается от cleanWs и почему cleanWs может быть недоступен?
Итог

Окружение в Jenkins - это слои: встроенные jenkins env от контроллера и плагинов, твоя статика в environment, твоя динамика и локальные значения в withEnv. Рабочее пространство - изолированный каталог сборки, которым управляют dir, ws и команды очистки deleteDir и cleanWs. Держи в голове, что каждый sh - отдельный процесс, секреты подставляй шеллом, а workspace чисти на входе и выходе. На эфемерных агентах в Kubernetes чистота приходит сама, но понимать механику всё равно надо - именно она объясняет добрую половину загадочных падений на CI. Актуально на момент Jenkins LTS 2.541.3 (июнь 2026).
👍4 ❤️ 🔥1 😄 🤔
Аватара пользователя
wiltanaka
Сообщения: 1
Зарегистрирован: 17 май 2026, 02:25

Re: Переменные среды, withEnv и рабочие пространства

Сообщение wiltanaka »

Спасибо, наконец дошло почему мой export пропадал между шагами. Собрал две команды в один sh с тройными кавычками и всё заработало.
👍1 ❤️1 🔥 😄 🤔
Аватара пользователя
torpilot
Сообщения: 1
Зарегистрирован: 02 июн 2026, 01:01

Re: Переменные среды, withEnv и рабочие пространства

Сообщение torpilot »

А cleanWs в post.cleanup или в post.always лучше ставить? У меня при падении хочется артефакты сохранить, но workspace всё равно подчистить.
👍 ❤️ 🔥2 😄 🤔
Ответить
← Предыдущая глава
Шаги оболочки: sh, bat, powershell и коды возврата
Следующая глава →
Работа с файлами, артефактами и отпечатками

Все главы курса «Jenkins: конвейеры CI/CD от основ до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «Jenkins: конвейеры CI/CD от основ до продакшена»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя