Код: Выделить всё
sh 'export TAG=1.2.3'Встроенные jenkins переменные среды: что доступно из коробки
Перед запуском любого шага Jenkins сам наполняет окружение служебными значениями. Это те самые jenkins env, которые доступны в каждом конвейере без всякой настройки. Самые ходовые:
- BUILD_NUMBER - порядковый номер текущей сборки, удобно для тегов образов и версий артефактов.
- JOB_NAME - имя джобы (с папками через слеш, если есть).
- WORKSPACE - абсолютный путь к рабочему каталогу на агенте, где развёрнут код.
- BUILD_URL и BUILD_ID - ссылка на сборку и её идентификатор.
- NODE_NAME - имя агента, на котором всё крутится.
Код: Выделить всё
checkout scmВ Declarative-конвейере обращайся к переменным через
Код: Выделить всё
envКод: Выделить всё
pipeline {
agent any
stages {
stage('Info') {
steps {
echo "Сборка #${env.BUILD_NUMBER} джобы ${env.JOB_NAME}"
echo "Код развёрнут в ${env.WORKSPACE}"
sh 'echo "Внутри shell: $BUILD_NUMBER на ветке $GIT_BRANCH"'
}
}
}
}
Код: Выделить всё
${env.BUILD_NUMBER}Код: Выделить всё
shКод: Выделить всё
$BUILD_NUMBERКод: Выделить всё
sh "echo ${env.BUILD_NUMBER}"
Свои переменные: 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')Шаг 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+ANYTHINGКод: Выделить всё
withEnv(["PATH+NODE=${env.WORKSPACE}/node_modules/.bin"]) {
sh 'eslint --version'
}
Рабочее пространство: jenkins workspace и изоляция сборок
jenkins workspace - это каталог на агенте, куда выгружается код и где выполняются шаги. По умолчанию путь привязан к джобе: для FreeStyle обычно JENKINS_HOME/workspace/имя-джобы, для пайплайнов агент сам выбирает каталог и кладёт его в
Код: Выделить всё
env.WORKSPACEНо изоляция не абсолютная. Если одна и та же джоба запустилась дважды параллельно на одном агенте, Jenkins создаст второй каталог с суффиксом
Код: Выделить всё
@2Код: Выделить всё
@3Сменить каталог на время помогает шаг dir. Он меняет текущую директорию для вложенных шагов и сам возвращает всё назад на выходе:
Код: Выделить всё
dir('build') {
sh 'cmake .. && make' // выполняется внутри WORKSPACE/build
}
sh 'pwd' // снова в корне workspace
Код: Выделить всё
ws("${env.WORKSPACE}/../shared-cache") {
sh 'ls -la'
}
Очистка 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()
}
}
}
Типичные грабли
- Переменная не видна между sh-шагами. Самая частая боль. и следом
Код: Выделить всё
sh 'export TAG=v1'- второй шаг пустой. Каждый sh - это отдельный процесс шелла, export живёт только внутри него. Решение: либо одинКод: Выделить всё
sh 'echo $TAG'на несколько команд, либо вытащить значение черезКод: Выделить всё
sh '''...'''в Groovy-переменную и прокинуть через env/withEnv.Код: Выделить всё
returnStdout: true - Присваивание env внутри script не везде долетает. работает в scripted и в блоке script, но логичнее статику класть в environment, а динамику - в withEnv.
Код: Выделить всё
env.FOO = 'bar' - Маскирование секретов ломается интерполяцией. подставит секрет ещё в Groovy, и в логе он засветится. Одинарные кавычки и
Код: Выделить всё
sh "curl -H 'token: ${TOKEN}'"- секрет подставит шелл, Jenkins его замаскирует.Код: Выделить всё
$TOKEN - Параллельные сборки и каталог @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 вычисли тег из и используй его в sh. После блока попробуй вывести переменную снова - убедись, что она исчезла.
Код: Выделить всё
git rev-parse --short HEAD - Добавь перед checkout и
Код: Выделить всё
deleteDir()в post.cleanup. Запусти дважды и проверь, что второй прогон стартует на чистом месте.Код: Выделить всё
cleanWs()
- Почему переменная, заданная через 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).