Ответ
В Jenkins я использую Declarative Pipeline с Groovy-подобным DSL, который пишется в Jenkinsfile. Это позволяет хранить конфигурацию как код (IaC) в репозитории.
Пример базового Jenkinsfile:
pipeline {
agent any
options {
timeout(time: 1, unit: 'HOURS')
}
environment {
DOCKER_REGISTRY = 'registry.example.com'
}
stages {
stage('Build') {
steps {
sh 'mvn clean package'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Deploy to Staging') {
when {
branch 'main'
}
steps {
sh 'kubectl apply -f k8s/staging/'
}
}
}
}
Ключевые элементы:
agent— определяет, где выполнять сборку (docker,kubernetes,label)stages/stage— логические этапы пайплайнаsteps— конкретные команды (shell, bat, script)post— постобработка (уведомления, очистка)environment— переменные окруженияparameters— параметры ручного запускаwhen— условное выполнение этапов
Для сложной логики использую Scripted Pipeline с полным Groovy, но Declarative предпочитаю из-за чёткой структуры и встроенных best practices.
Ответ 18+ 🔞
Слушай, а вот этот ваш Jenkins — ну просто ёперный театр какой-то, честное слово. Сидишь, пишешь эту хрень в Jenkinsfile, а в голове одна мысль: «Чёрта в душу, в сердцу мать! Ну зачем так сложно?». Но потом привыкаешь, и вроде даже удобно, что вся конфигурация как код лежит рядом с проектом, а не в этих интерфейсах, где кнопку случайно ткнёшь — и всё накрылось медным тазом.
Вот смотри, я в основном на Declarative Pipeline подсел. Это такой Groovy-подобный DSL, который выглядит более-менее человечно. Не то что этот Scripted, где можно нагородить такого, что сам потом будешь охуевать, пытаясь разобраться, что эта мартышлюшка год назад имела в виду.
Вот тебе пример простого Jenkinsfile, чтоб понятно было:
pipeline {
agent any
options {
timeout(time: 1, unit: 'HOURS')
}
environment {
DOCKER_REGISTRY = 'registry.example.com'
}
stages {
stage('Build') {
steps {
sh 'mvn clean package'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Deploy to Staging') {
when {
branch 'main'
}
steps {
sh 'kubectl apply -f k8s/staging/'
}
}
}
}
А теперь разжёвываю, что тут к чему, а то волнение ебать:
agent— это типа «где будем пахать». Можно сказать «на любом свободном рабе» (any), можно в докер-контейнере, а можно и на кубере — тут овердохуища вариантов. Главное — указать, а то пайплайн будет как хуй в пальто: вроде есть, а делать нихуя не может.stages/stage— это основные этапы, типа «Собрать», «Проверить», «Задеплоить». Всё логично.steps— вот тут уже конкретные команды: скрипты шелл, батники или свои Groovy-вставки. Тут и происходит вся магия, а иногда и пиздец, если команду кривую написал.post— хитрая жопа. Это действия после этапа: «всёгда», «успех», «падение». Идеально, чтобы отправить уведомление в слак, если всё сломалось, или артефакты сохранить. Без него как без рук.environment— переменные окружения. Удобно, чтобы не хардкодить урлы репозиториев или пароли (только пароли, ёпта, туда не пихай, для этого есть Credentials!).parameters— вот это прикольная штука. Позволяет при ручном запуске пайплайна спрашивать у пользователя: «Какую версию собрать?» или «На какой стенд задеплоить?». А то без этого некоторые товарищи — распиздяи редкостные — могут продакшен по ошибке тронуть.when— условное выполнение. Например, деплой на стейдж только если мержим вmain, а не с какой-нибудь левой веткиfeature-petuh-123. Экономит время и нервы.
Вообще, Declarative мне нравится за чёткость. Он как бы намекает: «Чувак, не выёбывайся, пиши по структуре». А Scripted — это полная воля, можно такие костыли навертеть, что потом доверия ебать ноль будет к этому коду. Хотя для какой-нибудь адской логики, где без цикла или сложной проверки — только он и спасает. Но если можно без этого — выбираю Declarative, и советую то же самое. Голова потом меньше болит.