Директива when: jenkins pipeline и условие на стейдже
when живёт внутри stage и решает один вопрос: запускать этот стейдж в текущей сборке или пропустить. Если условие ложное, Jenkins помечает стейдж как Skipped и идёт дальше - сборка при этом остаётся зелёной. Это ровно то, что нужно: один Jenkinsfile, разное поведение в зависимости от ветки, окружения или того, какие файлы изменились.
Самый частый кейс - jenkins when branch. Деплой только из main:
Код: Выделить всё
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make build'
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh './deploy.sh prod'
}
}
}
}
Кроме branch есть набор встроенных условий:
- branch - имя ветки, поддерживает маски: branch 'release/*' или branch comparator: 'REGEXP', pattern: 'release-.*'
- environment - проверка переменной: environment name: 'DEPLOY_ENV', value: 'production'
- expression - произвольное Groovy-выражение, которое должно вернуть true: expression { return params.RUN_DEPLOY == true }
- changeset - сработает, если в коммите менялись файлы по маске: changeset "**/*.sql" (удобно гонять миграции только когда они реально изменились)
- tag - сборка по git-тегу: tag "v*"
- buildingTag, triggeredBy - сборка тега и причина запуска
Код: Выделить всё
stage('Deploy prod') {
when {
allOf {
branch 'main'
environment name: 'DEPLOY_ENV', value: 'production'
not { changeset "docs/**" }
}
}
steps {
sh './deploy.sh prod'
}
}
Код: Выделить всё
when {
anyOf {
branch 'main'
triggeredBy 'UserIdCause'
}
}
Код: Выделить всё
stage('Deploy') {
agent { label 'deploy-node' }
when {
beforeAgent true
branch 'main'
}
steps { sh './deploy.sh' }
}

Блок post: jenkins post always и постобработка по результату
Стейдж выполнился (или упал) - что дальше? Уведомить, заархивировать артефакты, прибрать за собой. Этим занимается блок post. Он ставится либо в конце конкретного stage (постобработка этого стейджа), либо в конце pipeline (итог всей сборки). Внутри - условия по статусу, и Jenkins сам выберет, какие из них запустить.
Полный набор условий post:
- always - выполнится всегда, чем бы сборка ни кончилась. Сюда кладут очистку и то, что должно произойти при любом исходе
- success - только при успехе
- failure - только при падении (статус FAILURE)
- unstable - сборка нестабильна (обычно упали тесты, но шаг не зафейлился жёстко)
- aborted - сборку прервали вручную или по таймауту
- unsuccessful - любой неуспех: failure, unstable или aborted
- changed - статус изменился относительно прошлой сборки (была красная, стала зелёная - или наоборот)
- fixed - прошлая сборка была неуспешной, а эта успешна (чинили - починили)
- regression - прошлая была успешной, эта сломалась (то, на что хочется орать алертом)
- cleanup - выполняется самым последним, после всех остальных условий, для финальной уборки
Код: Выделить всё
post {
always {
cleanWs()
}
success {
echo 'Сборка прошла успешно'
}
failure {
mail to: 'team@example.com',
subject: "Упала сборка ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Смотри логи: ${env.BUILD_URL}"
}
unstable {
echo 'Часть тестов красная - смотри отчёт'
}
fixed {
echo 'Сборка снова зелёная после падения'
}
}
script-блок и сочетание when + post в реальном деплое
Declarative намеренно ограничен - ты описываешь структуру, а не пишешь программу. Но иногда нужна логика: цикл, try/catch, сложное ветвление. Для этого есть аварийный люк - script. Внутри него работает полноценный Groovy (scripted pipeline), но держи такие блоки маленькими: чем больше Groovy в declarative, тем хуже читается конвейер.
Код: Выделить всё
stage('Notify') {
steps {
script {
def color = currentBuild.currentResult == 'SUCCESS' ? 'good' : 'danger'
def msg = "Сборка ${env.JOB_NAME} #${env.BUILD_NUMBER}: ${currentBuild.currentResult}"
slackSend channel: '#ci', color: color, message: msg
}
}
}
Код: Выделить всё
pipeline {
agent any
environment {
DEPLOY_ENV = 'production'
}
stages {
stage('Build') {
steps { sh 'make build' }
}
stage('Test') {
steps { sh 'make test' }
}
stage('Deploy prod') {
when {
beforeAgent true
allOf {
branch 'main'
environment name: 'DEPLOY_ENV', value: 'production'
}
}
steps {
sh './deploy.sh prod'
}
post {
success {
slackSend channel: '#deploys',
message: "Прод задеплоен: ${env.BUILD_URL}"
}
failure {
slackSend channel: '#deploys', color: 'danger',
message: "ДЕПЛОЙ УПАЛ: ${env.BUILD_URL}"
}
}
}
}
post {
always {
cleanWs()
}
regression {
mail to: 'team@example.com',
subject: "Регрессия в ${env.JOB_NAME}",
body: "Стабильная сборка сломалась: ${env.BUILD_URL}"
}
}
}
Типичные грабли
- branch не срабатывает в обычном job. Условие branch требует BRANCH_NAME, а её ставит только multibranch pipeline. В одиночном задании проверяй ветку через expression и git rev-parse либо параметр.
- post стейджа не выполняется при пропуске. Если стейдж пропущен по when, его внутренний post не запустится - там нечего постобрабатывать. Алерты о деплое вешай на post того стейджа, который реально шёл, или на post всего pipeline.
- beforeAgent забыт - и под поднимается зря. Особенно больно в Kubernetes: без beforeAgent true Jenkins создаёт эфемерный pod, и только потом видит, что when ложный. Деньги и время на ветер.
- success в post не значит "всё хорошо". Если внутри шага ты сделал catchError или тесты пометили сборку UNSTABLE, итог - не SUCCESS. Проверяй currentBuild.currentResult, а не свои ожидания.
- changeset на первой сборке ветки. Когда у ветки ещё нет предыдущего коммита для сравнения, changeset может вести себя неожиданно. Не строй на нём критичный деплой без запасного условия.
- Возьми Jenkins LTS актуальной линии (2.541.x на июнь 2026; учти, что новые линии 2.555+ уже требуют Java 21 или 25). Создай multibranch pipeline на любом git-репозитории с ветками main и dev.
- Напиши Jenkinsfile со стейджами Build, Test, Deploy. На Deploy повесь when { branch 'main' } с beforeAgent true. Запусти сборку из dev - убедись, что Deploy помечен Skipped.
- Добавь блок post на уровне pipeline: always с echo, success и failure с разными сообщениями. Сломай шаг (например sh 'exit 1') и проверь, что отработал именно failure, а не success.
- Замени одно условие на allOf из branch и expression, протестируй обе ветки логики.
- Добавь script-блок, который пишет в лог currentBuild.currentResult, и посмотри значение при упавшей и при успешной сборке.
- Чем отличается результат, когда стейдж пропущен по when, от ситуации, когда он упал? Что происходит со статусом всей сборки?
- Зачем нужна опция beforeAgent true и где её отсутствие особенно дорого обходится?
- В чём разница между условиями post always, changed и regression? Когда какое сработает?
- Почему post внутри стейджа не выполнится, если этот стейдж пропущен по when, и куда тогда вешать нотификацию о деплое?
when отвечает на вопрос "выполнять ли стейдж", post - "что сделать по итогу". Связка branch плюс post.failure/success - это стандартный шаблон безопасного деплоя: выкатываем только из main, шумим в чат при падении, всегда прибираемся за собой. Добавь beforeAgent true, чтобы не жечь агентов впустую, держи script-блоки короткими - и твой declarative-конвейер будет одновременно читаемым и боевым. Дальше эти же when и post лягут в основу матричных сборок и параллельных стейджей, которые разберём в следующих главах.