Файлы внутри сборки: рабочая директория и шаги чтения/записи
Каждый jenkins build живёт в своём workspace - рабочей директории на агенте. Туда клонируется код, туда падают результаты компиляции. Пока сборка идёт, ты можешь читать и писать файлы прямо из пайплайна, не вызывая голый shell.
Базовый набор шагов:
- readFile - прочитать файл в строку
- writeFile - записать строку в файл
- fileExists - проверить, что файл есть (вернёт true/false)
Код: Выделить всё
pipeline {
agent any
stages {
stage('Prepare') {
steps {
script {
// записываем номер сборки в файл
writeFile file: 'build-info.txt',
text: "build ${env.BUILD_NUMBER}\n"
// читаем обратно
def info = readFile('build-info.txt').trim()
echo "Прочитали: ${info}"
if (fileExists('pom.xml')) {
echo 'Это maven-проект'
}
}
}
}
}
}

stash и unstash: передача файлов между стейджами и агентами
Самая частая засада многоагентных пайплайнов: собрал на одном, тестируешь на другом, а файлов нет. Решение - jenkins stash. Шаг stash упаковывает выбранные файлы в архив и складывает его на контроллере. Потом на любом другом агенте шаг unstash распаковывает их обратно в workspace.
Код: Выделить всё
pipeline {
agent none
stages {
stage('Build') {
agent { label 'maven' }
steps {
sh 'mvn -B clean package'
// прячем собранный jar для следующих стейджей
stash name: 'app-jar', includes: 'target/*.jar'
}
}
stage('Test') {
agent { label 'tester' }
steps {
// достаём jar на другом агенте
unstash 'app-jar'
sh 'java -jar target/app.jar --selftest'
}
}
}
}
Код: Выделить всё
agent noneКод: Выделить всё
agent { label ... }У stash есть параметры
Код: Выделить всё
includesКод: Выделить всё
excludesКод: Выделить всё
nameВажные грабли: stash едет через контроллер и хранится у него в памяти/на диске. Это не транспорт для гигабайтных артефактов. Держи стэши маленькими - конфиги, один-два бинаря, отчёты. Тяжёлые сборки (целые node_modules, докер-слои) гоняй через внешнее хранилище или registry, а не через stash. По умолчанию стэши удаляются в конце сборки. Если хочешь, чтобы при рестарте отдельного стейджа они были доступны, включи опцию:
Код: Выделить всё
options {
preserveStashes(buildCount: 5)
}
stash живёт внутри одной сборки. А что отдать наружу, чтобы скачать через UI или забрать на деплой? Для этого есть jenkins artifacts и шаг archiveArtifacts. Он копирует файлы из workspace в постоянное хранилище сборки на контроллере, и они доступны на странице build 393 как ссылки для скачивания - переживают очистку workspace.
Код: Выделить всё
stage('Archive') {
steps {
archiveArtifacts artifacts: 'target/*.jar',
fingerprint: true,
onlyIfSuccessful: true
}
}
- artifacts - glob-маска файлов (можно несколько через запятую)
- fingerprint: true - снять отпечаток (об этом ниже)
- onlyIfSuccessful: true - архивировать только если сборка успешна
- allowEmptyArchive: true - не падать, если по маске ничего не нашлось
- excludes - что исключить из маски
Код: Выделить всё
stage('Test') {
steps {
sh 'mvn -B test'
}
post {
always {
junit testResults: '**/target/surefire-reports/*.xml',
allowEmptyResults: true
}
}
}
Код: Выделить всё
post { always { ... } }Код: Выделить всё
allowEmptyResults: trueОтпечатки (fingerprints): где использован мой артефакт
Теперь самое недооценённое - jenkins fingerprint. Отпечаток это MD5-хэш файла, который Jenkins запоминает вместе со ссылкой, в какой сборке и какого job этот файл появлялся или использовался. Включив
Код: Выделить всё
fingerprint: trueЗачем это на практике. Представь, что job "library" собирает common.jar, а пять других job'ов его потребляют. Прилетел баг. По отпечатку common.jar ты мгновенно видишь, какие именно сборки и каких версий тянули проблемный файл, и кого надо пересобрать. Без отпечатков пришлось бы вручную сопоставлять версии по логам.
Снять отпечаток можно не только при архивации, но и отдельным шагом:
Код: Выделить всё
fingerprint 'target/*.jar'
Очистка и передача дальше по пайплайну
Workspace надо подчищать, особенно на долгоживущих статических агентах, где мусор копится между сборками. Простой способ - плагин Workspace Cleanup и шаг
Код: Выделить всё
cleanWs()Код: Выделить всё
post {
always {
cleanWs()
}
}
Передача артефакта дальше по конвейеру - это связка из уже разобранного. Внутри одной сборки гоняй файлы через stash/unstash. Наружу и в следующие job'ы (например, отдельный pipeline деплоя через
Код: Выделить всё
build job: 'deploy', ...Для сравнения: в GitLab CI ту же роль играют
Код: Выделить всё
artifacts:Код: Выделить всё
cache:Код: Выделить всё
actions/upload-artifactКод: Выделить всё
download-artifactТипичные грабли
- Гоняешь сотни мегабайт через stash и удивляешься, что контроллер тормозит. stash - для мелочи, не для тяжёлых сборок.
- archiveArtifacts без валит сборку, когда маска ничего не нашла (опечатка в пути к target).
Код: Выделить всё
allowEmptyArchive - junit вне - при падении тестов отчёт не публикуется, и в UI пусто.
Код: Выделить всё
post { always } - Забыл и потом не можешь понять, какая сборка тянула битый jar.
Код: Выделить всё
fingerprint: true - Ждёшь файлы на втором агенте без unstash - их там нет, у каждого агента свой workspace.
- Путаешь stash и archiveArtifacts: первый живёт внутри сборки, второй сохраняется наружу надолго.
Собери пайплайн с
Код: Выделить всё
agent none- Build на одном агенте: создай файл version.txt с
Код: Выделить всё
writeFile, собери любой jar (или простоКод: Выделить всё
env.BUILD_NUMBER), сделай stash обоих файлов.Код: Выделить всё
sh 'echo test > app.jar' - Test на другом агенте: unstash, проверь , прочитай version.txt через readFile.
Код: Выделить всё
fileExists('app.jar') - Archive: archiveArtifacts с , добавь junit с фейковым XML-отчётом в post.
Код: Выделить всё
fingerprint: true
Контрольные вопросы
- Чем stash принципиально отличается от archiveArtifacts и когда какой использовать?
- Почему шаг junit ставят в блок post { always }, а не просто после тестов?
- Что даёт параметр fingerprint: true и какую задачу решают отпечатки между разными job?
- Почему передавать гигабайтные бинари через stash - плохая идея, и чем заменить?
Файлы в Jenkins живут в workspace и по умолчанию недолговечны. readFile/writeFile/fileExists работают с ними внутри стейджа, stash/unstash переносят между агентами в рамках сборки, archiveArtifacts сохраняет результаты наружу, junit публикует тесты, а fingerprint даёт сквозную трассировку артефакта по всем job. Освой эту связку - и build 393 перестанет терять твои бинари, а ты всегда будешь знать, где какой артефакт всплыл.