В этом уроке разберём, как устроена секция post в declarative pipeline, какие у неё условия (always, success, failure и компания), как гарантированно чистить workspace через cleanWs, публиковать результаты тестов через junit, архивировать артефакты и слать jenkins уведомления так, чтобы они доходили только когда надо.
Зачем нужен блок post и как он работает
Главная идея: шаги внутри stages выполняются, только пока всё хорошо. Упал тест, свалился sh с ненулевым кодом - и остаток pipeline просто не запускается. А что если именно при падении нужно отправить алёрт и собрать логи? Если писать это обычными шагами в конце, до них дело не дойдёт. Любой jenkins build может оборваться в середине, и постобработка должна сработать в любом случае.
Секция post решает ровно это. Она объявляется на уровне всего pipeline или внутри отдельного stage и выполняется ПОСЛЕ основной работы - независимо от того, успех там или провал. По сути это аналог try/finally из обычного программирования: что бы ни случилось в теле, finally отработает. В scripted-конвейере так и пишут руками, через try/catch/finally, - к этому вернёмся ниже. В declarative за тебя это делает движок: ты просто описываешь, что и при каком исходе делать.
Внутри post живут не произвольные шаги, а условные блоки. Вот полный набор на 2026 год:
- always - выполняется всегда, при любом статусе. Сюда кладут очистку и то, что нужно сделать безусловно.
- success - только если сборка зелёная.
- failure - только если сборка упала (статус FAILURE). Главный кандидат для жёстких алёртов.
- unstable - сборка "нестабильна": обычно это упавшие тесты или нарушения качества кода, помеченные жёлтым.
- aborted - сборку прервали (обычно вручную или по таймауту).
- unsuccessful - всё, что НЕ success: объединяет failure, unstable и aborted.
- changed - статус отличается от предыдущего запуска (был success, стал failure - или наоборот).
- fixed - починилось: текущая сборка успешна, а предыдущая была failure или unstable.
- regression - регрессия: текущая упала/нестабильна/прервана, а предыдущая была успешной. Самый ценный сигнал - "только что сломали".
- cleanup - выполняется в самом конце, ПОСЛЕ всех остальных условий, всегда. Финальная уборка.

Практика: junit, archiveArtifacts и уведомления в post
Соберём типовой declarative pipeline, который собирает Java-проект, гоняет тесты и аккуратно завершается. Обрати внимание, как jenkins post-секция распределяет действия по исходам.
Код: Выделить всё
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './gradlew clean assemble'
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
}
post {
always {
// публикуем отчёты JUnit в любом случае - даже если тесты упали
junit testResults: '**/build/test-results/**/*.xml', allowEmptyResults: true
}
success {
// артефакты сохраняем только у зелёной сборки
archiveArtifacts artifacts: 'build/libs/*.jar', fingerprint: true
echo 'Сборка успешна, артефакты заархивированы'
}
failure {
// jenkins post failure - сюда вешаем алёрт
mail to: 'team@example.com',
subject: "СБОРКА УПАЛА: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Смотри лог: ${env.BUILD_URL}console"
}
unstable {
echo 'Тесты красные - сборка нестабильна'
}
cleanup {
cleanWs()
}
}
}
Про уведомления: шаг mail - встроенный, без плагинов, но примитивный. На практике в 2026-м чаще используют slackSend (плагин Slack Notification), Telegram-интеграции или общую функцию-обёртку. Удобный приём - вынести логику в одну функцию notify и дёргать её из разных условий:
Код: Выделить всё
failure {
script {
notify('danger', "Упала: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
}
}
fixed {
script {
notify('good', "Починилась: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
}
}
Отдельно про статус. Внутри post часто нужно знать, чем всё закончилось. Текущий результат лежит в currentBuild.result (значения SUCCESS, FAILURE, UNSTABLE, ABORTED или null, если ещё не задан). Можно и принудительно пометить сборку:
Код: Выделить всё
always {
script {
if (currentBuild.result == 'UNSTABLE') {
echo 'Есть красные тесты, но билд не валим'
}
echo "Итоговый статус: ${currentBuild.currentResult}"
}
}
Постобработка в scripted: try/catch/finally вручную
Declarative post - это сахар над тем, что в scripted-конвейере пишется руками. Если по теме нужна динамика (генерация stage в цикле, сложная логика), берут scripted, и тогда постобработку оборачивают сам:
Код: Выделить всё
node {
try {
stage('Build') {
sh './gradlew clean assemble'
}
stage('Test') {
sh './gradlew test'
}
currentBuild.result = 'SUCCESS'
} catch (err) {
currentBuild.result = 'FAILURE'
throw err // пробрасываем, чтобы сборка честно покраснела
} finally {
junit '**/build/test-results/**/*.xml'
// jenkins cleanup - гарантированная очистка в finally
cleanWs()
}
}
Типичные грабли
- junit в success вместо always. Тесты упали, статус FAILURE, блок success не сработал - и отчёта о тестах нет именно тогда, когда он нужнее всего. Всегда публикуй результаты в always.
- cleanWs() в always, а не в cleanup. Очистка может снести файлы раньше, чем archiveArtifacts или junit успеют их прочитать. Уборку - в cleanup, он последний.
- cleanWs() требует плагин. Шаг даёт Workspace Cleanup Plugin. Без него получишь "No such DSL method 'cleanWs'". Поставь плагин или, в простом случае, используй deleteDir() внутри блока (он чистит текущую директорию).
- Алёрты на каждый запуск. Если уведомление висит в always, команда быстро научится его игнорировать. Шли в failure/regression, а "ожило" - через fixed.
- archiveArtifacts с allowEmptyArchive по умолчанию false. Если артефакта нет (а при падении его и не будет), шаг сам уронит сборку. Либо держи его в success, либо ставь allowEmptyArchive: true.
- Путаница result и currentResult. currentBuild.result бывает null. Для проверок в post надёжнее currentBuild.currentResult.
Повтори руками, чтобы прочувствовать механику:
- Возьми любой проект с тестами. Напиши declarative pipeline со стадиями Build и Test.
- Добавь post с блоками always (junit), success (archiveArtifacts), failure (echo или mail) и cleanup (cleanWs).
- Запусти зелёным - убедись, что артефакт заархивирован, а failure не сработал.
- Сломай тест намеренно. Перезапусти. Проверь: отчёт junit опубликован (always отработал), failure сработал, success - нет.
- Почини тест и запусти снова - проверь, как ведёт себя fixed (если добавишь этот блок).
- Перепиши тот же конвейер в scripted с try/catch/finally и сравни поведение.
- Чем отличается блок always от cleanup и почему очистку лучше вешать на cleanup?
- В какой блок post поставить шаг junit, чтобы отчёт о тестах публиковался даже при их падении, и почему?
- Какому конструкту обычного языка соответствует declarative post и как это выглядит в scripted-конвейере?
- В чём разница между currentBuild.result и currentBuild.currentResult и какое поле надёжнее в post?
Секция post - это твой finally для всего конвейера: она отрабатывает при любом исходе и раскладывает действия по условиям от always до cleanup. Базовый рабочий набор прост: junit в always, archiveArtifacts в success, уведомление в failure (и fixed для "ожило"), а cleanWs - в cleanup, чтобы уборка была гарантированной и последней. Понимаешь, что под капотом это try/catch/finally, - значит, разберёшь и declarative, и scripted. Дальше будет проще: на этом фундаменте строятся и матрицы окружений, и параллельные стадии.