Основи роботи з Linux. / Jenkins. Початок роботи. / Jenkins. Скрипти на groovy.
Jenkins. Скрипти на groovy.
Послідовність операцій складання або тестування може бути оформлена у вигляді pipeline (конвейера) в окремому файлі Jenkinsfile і покладена в репозиторій.
Це дозволить:
-
автоматично згенерувати конвейєр для всіх бранч і pull request-ів.
-
тримати логіку тестів у проекті для аудиту
Jenkinsfile скрипт може бути написаний у декларативному або імперативному стилі.
Декларативний стиль передбачає використання високорівневих конструкцій мови, які вирішують комплексні завдання за один раз.
При імперативному підході кожну задачу потрібно докладніше описувати.
Приклад як може виглядати процес складання, тестування та деплою проекту.
Конвейєр складається з наступних елементів.
нода (node) - сервер, на якому все виконується
стадія (stage) - блок, що визначає набір операцій для одного завдання (складання тестування, деплою і т.д.). Ці стадії потім будуть красиво оформлені інтерфейсом Jenkins із зазначенням часу, статусу та ін.
крок (step) - одна операція. Фактично вказівка що робити у певний час.
Розглянемо варіанти оформлення Jenkinsfile.
Приклад оформлення конвейєра у декларативному стилі.
pipeline {
agent any
stages {
stage('Build') {
steps {
//
}
}
stage('Test') {
steps {
//
}
}
stage('Deploy') {
steps {
//
}
}
}
}
agent any - говорить про те, що виконувати потрібно на будь-якому агенті
Для декларативних скриптів застосовується агент, для імперативних нод.
Приклад імперативного підходу.
node('node name') {
stage('Build') {
//
}
stage('Test') {
//
}
stage('Deploy') {
//
}
}
Розглянемо ще один декларативний приклад
pipeline {
agent any
options {
skipStagesAfterUnstable()
}
stages {
stage('Build') {
steps {
sh 'make'
}
}
stage('Test'){
steps {
sh 'make check'
junit 'reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh 'make publish'
}
}
}
}
pipeline - кореневий блок всього конвейєра
agent - сутність, яка виконуватиме код (нода, або комп’ютер, або докер образ)
sh - виконує shell команду
junit - плагін, що збирає статистику та формує xml звітпосилання
Встановлення агента.
Агент вказуватиме, де саме виконувати роботу.
agent any
виконає на будь-якому доступному агенті
agent none
При цьому ми змушуємо прописувати агента для кожного кроку, а для конвеєра встановлюємо в none.
agent { label 'my-defined-label' }
вибере агента з мітки
agent { node { label 'labelName' } }
Працює так само як і з міткою, але дозволяє використовувати додаткові налаштування для ноди, наприклад customWorkspace.
agent {
node {
label 'my-defined-label'
customWorkspace '/some/other/path'
}
}
customWorkspace може бути як відносним так і абсолютним шляхом та дозволяє вийти за межі дефолтного воркспейсу (робочого каталогу)
Агент для докеру зображення.
agent {
docker {
image 'maven:3-alpine'
label 'my-defined-label'
args '-v /tmp:/tmp'
}
}
args - передає аргумени команді docker run
Зображення можна зібрати з файлу Dockerfile
agent {
dockerfile {
filename 'Dockerfile.build'
dir 'build'
label 'my-defined-label'
additionalBuildArgs '--build-arg version=1.0.2'
args '-v /tmp:/tmp'
}
}
Те ж, що запуск
"docker build -f Dockerfile.build --build-arg version=1.0.2 ./build/
Приклад запуску команди всередині контейнера у декларативному стилі.
pipeline {
agent { docker 'maven:3-alpine' }
stages {
stage('Example Build') {
steps {
sh 'mvn -B clean verify'
}
}
}
}
Агент можна визначати для різних стадій.
pipeline {
agent none
stages {
stage('Example Build') {
agent { docker 'maven:3-alpine' }
steps {
echo 'Hello, Maven'
sh 'mvn --version'
}
}
stage('Example Test') {
agent { docker 'openjdk:8-jre' }
steps {
echo 'Hello, JDK'
sh 'java -version'
}
}
}
}
Конструкція post
Дозволяє виконати код за певних обставин, умов або результатів виконання стадій (залежно де його всунути).
Умови.
allways - виконається завжди
failure - тільки коли провалений
success - тільки коли успішно виконано
changed - тільки якщо результати відрізняються від попереднього запуску
fixed - якщо попередній запуск був провалений, а поточний ні
regression - навпаки
приклад.
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
post {
always {
echo 'I will always say Hello again!'
}
}
}
Можна визначати одразу кілька варіантів.
pipeline {
agent any
stages {
stage('Test') {
steps {
sh 'echo "Fail!"; exit 1'
}
}
}
post {
always {
echo 'This will always run'
}
success {
echo 'This will run only if successful'
}
failure {
echo 'This will run only if failed'
}
unstable {
echo 'This will run only if the run was marked as unstable'
}
changed {
echo 'This will run only if the state of the Pipeline has changed'
echo 'For example, if the Pipeline was previously failing but is now successful'
}
}
}
Переменные окружения. Директива environment.
Пары ключ - значение, которые передадуться внутрь каждого шага.
pipeline {
agent any
environment {
CC = 'clang'
}
stages {
stage('Example') {
environment {
AN_ACCESS_KEY = credentials('my-predefined-secret-text')
}
steps {
sh 'printenv'
}
}
}
}
Як бачимо, існує ієрархія видимості змінних залежно куди їх втикати до всього конвейєра або конкретного кроку.
Ще приклад виведення змінних оточення.
pipeline {
agent {
label '!windows'
}
environment {
DISABLE_AUTH = 'true'
DB_ENGINE = 'sqlite'
}
stages {
stage('Build') {
steps {
echo "Database engine is ${DB_ENGINE}"
echo "DISABLE_AUTH is ${DISABLE_AUTH}"
sh 'printenv'
}
}
}
}
Директива options.
Дозволяє конфігурувати деякі корисні опції під час виконання конвейєра.
buildDiscarder - керує збереженням артифактів складання та виведення в консоль
checkoutToSubdirectory - перевіряє наявність довільного каталогу
options { checkoutToSubdirectory('foo') }
disableConcurrentBuilds - допомагає виключити одночасне використання ресурсу, що розділяється
newContainerPerStage - створюватиме за новим контейнером на кожну стадію
timeout - тайм по якому потрібно перервати конвейєр
pipeline {
agent any
options {
timeout(time: 1, unit: 'HOURS')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
retry - скільки разів повторити після провалу
Опції можна встановлювати як до всього конвейєра, так і до певних стадій.
Тригери
Визначають умову, за якої конвейєр повинен бути запущений.
cron - визначає розклад для запусків
triggers { cron('H */4 * * 1-5') }
pollSCM - визначає кроновий інтервал для перевірки змін у вихідниках. Якщо зміни виявлено, запускається конвейєр.
triggers { pollSCM('H */4 * * 1-5') }
upstream - приймає рядок із завданнями через кому та поріг. Якщо хоч одне завдання завершилося із зазначеним порогом, то конвейєр перезапуститься.
triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }
приклад.
pipeline {
agent any
triggers {
cron('H */4 * * 1-5')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
triggers{ cron(‘H/15 * * * *’) } - кожні 15 хв.
triggers{ cron(‘H(0-29)/10 * * * *’) } - кожні 10 хв у першій половині години
Директива інструментів.
Дозволяє встановити програми на конвейєр.
pipeline {
agent any
tools {
maven 'apache-maven-3.0.1'
}
stages {
stage('Example') {
steps {
sh 'mvn --version'
}
}
}
}
Директива введення.
Дозволяє забирати дані, що вводяться користувачем.
pipeline {
agent any
stages {
stage('Example') {
input {
message "Should we continue?"
ok "Yes, we should."
submitter "alice,bob"
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
}
}
steps {
echo "Hello, ${PERSON}, nice to meet you."
}
}
}
}
Наприклад, ми можемо використовувати це для отримання дозволу на деплой коду на продакшин сервер після успішних тестів.
Директива when
Визначає умову, за якої потрібно виконувати стадію.
Наприклад, якщо бранч відповідає масці.
when { branch pattern: "release-\\d+", comparator: "REGEXP"}
Коли білд містить тег.
when { buildingTag() }
Коли в лозі міститься рядок, що шукається.
when { changelog '.*^\\[DEPENDENCY\\] .+$' }
Якщо вихідники містять файли, що цікавлять
when { changeset "**/*.js" }
За запитами Pull Request
when { changeRequest() }.
Якщо є певна змінна в оточенні.
when { environment name: 'DEPLOY_TO', value: 'production' }
Якщо є тег
when { tag "release-*" }.
Заперечення
when { not { branch 'master' } }
Якщо спрацював певний тригер
when { triggeredBy 'TimerTrigger' }
приклад.
pipeline {
agent any
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
branch 'production'
}
steps {
echo 'Deploying'
}
}
}
}
Умови можна поєднувати.
when { branch ‘production’ environment name: ‘DEPLOY_TO’, value: ‘production’ }
Або влагоджувати один одного.
when {
allOf {
branch 'production'
environment name: 'DEPLOY_TO', value: 'production'
}
}
Послідовні стадії
Стадії можуть бути не тільки вкладеними, а й визначати послідовність за допомогою директив steps, stages, parallel або matrix.
Паралельне виконання (paralel).
stage('Parallel In Sequential') {
parallel {
stage('In Parallel 1') {
steps {
echo "In Parallel 1"
}
}
stage('In Parallel 2') {
steps {
echo "In Parallel 2"
}
}
}
}
stage может содержать только один блок paralel.
Установка параметра failFast true дозволяє перервати виконання у разі провалу з стадій.
stage('Parallel Stage') {
when {
branch 'master'
}
failFast true
parallel {
stage('Branch A') {
agent {
label "for-branch-a"
}
steps {
echo "On Branch A"
}
}
....
Matrix
Дозволяє конструювати матрицю із осередків.
Наприклад, так можна створити матрицю 4 на 3 з 12 значень.
matrix {
axes {
axis {
name 'PLATFORM'
values 'linux', 'mac', 'windows'
}
axis {
name 'BROWSER'
values 'chrome', 'edge', 'firefox', 'safari'
}
}
// ...
}
Якщо тепер вкласти всередину стадії, то вони виконаються для кожного осередку послідовно.
matrix {
axes {
axis {
name 'PLATFORM'
values 'linux', 'mac', 'windows'
}
}
stages {
stage('build') {
// ...
}
stage('test') {
// ...
}
stage('deploy') {
// ...
}
}
}
Директива script
Всередину цієї директиви може бути вкладений код groovy та виконаний на рівні одного кроку.
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
}
}
Умови
node {
stage('Example') {
if (env.BRANCH_NAME == 'master') {
echo 'I only execute on the master branch'
} else {
echo 'I execute elsewhere'
}
}
}
Імперативні скрипти.
Винятки
node {
stage('Example') {
try {
sh 'exit 1'
}
catch (exc) {
echo 'Something failed, I should sound the klaxons!'
throw
}
}
}