====== Pipelines ======
===== Общее =====
if (params.getOrDefault('BOOLEAN_PARAM_NAME', true))
if (params.CREDENTIAL_ID.toString().isEmpty()) {
currentBuild.result = 'ABORTED'
error("CREDENTIAL_ID is empty")
}
===== Переменные =====
:!: Примеры
pipeline {
agent any
stages {
stage("Variables") {
steps {
my_var = sh label: 'my echo', returnStdout: true, script: 'echo edede'
}
}
stage("Variables") {
steps {
script{
// хотя хз, чет не сработало
script: '''
echo "$my_var"
'''
}
}
}
}
}
==== Переменные среды ====
[[https://e.printstacktrace.blog/jenkins-pipeline-environment-variables-the-definitive-guide/|Дока]]
Глобальная переменная, доступная через **env**. Объявляется в любом месте, либо в блоке **environment** либо в **script**\\
Хранится только в формате строки, в т.ч. и булевы значения\\
Обратится можно и без "env" но если нет обычной переменной с таким же именем иначе будет путаница\\
:!: Лист всех переменных
pipeline {
agent any
stages {
stage("Env Variables") {
steps {
sh "printenv"
// можно
sh "printenv | sort"
}
}
}
}
:!: Установка переменных
Переменные среды могут быть установлены декларативно с помощью environment { }блока, императивно с помощью env.VARIABLE_NAMEили с помощью withEnv(["VARIABLE_NAME=value"]) {}блока.
pipeline {
agent any
environment {
FOO = "bar"
}
stages {
stage("Env Variables") {
environment {
NAME = "Alan"
}
steps {
echo "FOO = ${env.FOO}"
echo "NAME = ${env.NAME}"
script {
env.TEST_VARIABLE = "some test value"
}
echo "TEST_VARIABLE = ${env.TEST_VARIABLE}"
withEnv(["ANOTHER_ENV_VAR=here is some value"]) {
echo "ANOTHER_ENV_VAR = ${env.ANOTHER_ENV_VAR}"
}
}
}
}
}
:!: Переопределение переменных
* Блок withEnv(["env=value]) { }может переопределить любую переменную среды.
* Переменные, заданные с помощью environment {}блока, не могут быть переопределены с помощью императивного env.VAR = "value"присваивания.
* Императивное env.VAR = "value"присваивание может переопределить только переменные среды, созданные с помощью императивного присваивания.
pipeline {
agent any
environment {
FOO = "bar"
NAME = "Joe"
}
stages {
stage("Env Variables") {
environment {
NAME = "Alan" // overrides pipeline level NAME env variable
BUILD_NUMBER = "2" // overrides the default BUILD_NUMBER
}
steps {
echo "FOO = ${env.FOO}" // prints "FOO = bar"
echo "NAME = ${env.NAME}" // prints "NAME = Alan"
echo "BUILD_NUMBER = ${env.BUILD_NUMBER}" // prints "BUILD_NUMBER = 2"
script {
env.SOMETHING = "1" // creates env.SOMETHING variable
}
}
}
stage("Override Variables") {
steps {
script {
env.FOO = "IT DOES NOT WORK!" // it can't override env.FOO declared at the pipeline (or stage) level
env.SOMETHING = "2" // it can override env variable created imperatively
}
echo "FOO = ${env.FOO}" // prints "FOO = bar"
echo "SOMETHING = ${env.SOMETHING}" // prints "SOMETHING = 2"
withEnv(["FOO=foobar"]) { // it can override any env variable
echo "FOO = ${env.FOO}" // prints "FOO = foobar"
}
withEnv(["BUILD_NUMBER=1"]) {
echo "BUILD_NUMBER = ${env.BUILD_NUMBER}" // prints "BUILD_NUMBER = 1"
}
}
}
}
}
==== Переменные ====
Обращаться к переменным следует всегда с префиксами если они есть, например для переменных окружения это "env", для входных параметров это "params". т.к. переменная легко может подменится локальной с таким же именем, не имеющей отношения к переменной окружения или параметру\\
:!: Работа с переменными, модуль sh
Модуль **sh** по всей видимости выполняется напрямую в оболочке, поэтому ему доступны только переменные окружения среды\\
Задать переменные окружения из скрипта можно в блоке **Environment**\\
// Входящий параметр (видимо по умолчанию доступны в переменных окружения)
stage('Clone source') {
steps {
echo params.MY_BRANCH
sh label: 'repo sync', script: '''
repo init -u ssh://myrepository.ru
repo sync
if [ $MY_BRANCH != "master" ] ; then
repo forall -c git checkout $MY_BRANCH
fi
'''
}
}
// Объявление в скрипте
stage('Increment & push MGA_TAG') {
environment{
myLocalVar = getValueFromFunction()
}
steps {
script{
if (env.myLocalVar == null || env.myLocalVar.equals("")) {
currentBuild.result = 'ABORTED'
}
}
sh label: 'Push tag if it is set',
script: '''
cd $WORKSPACE/igas
repo forall -c git tag -a $myLocalVar -m $myLocalVar
repo forall -c git push origin "$myLocalVar"
'''
}
}
//
// Или вот пример
stage('Build') {
environment {
VERSION_STRING = new SimpleDateFormat("yyyy_MM_dd_HH_mm").format(new Date())
}
steps {
sh label: 'my scritp', script: '''
echo variable in other block1 - $VERSION_STRING
echo variable in other block2 - "$VERSION_STRING"
echo variable in other block3 - '$VERSION_STRING' <-- эта не раскрывается
'''
}
}
// Объявление входных параметров
def setParameters() {
properties([
parameters([
gitParameter(
name: 'MY_BRANCH',
description: 'Тег или ветка, откуда делать сборку',
branch: '',
branchFilter: '.*',
defaultValue: 'origin/master',
listSize: '10',
quickFilterEnabled: false,
requiredParameter: true,
selectedValue: 'DEFAULT',
sortMode: 'ASCENDING_SMART',
tagFilter: '[0-9]*',
type: 'PT_BRANCH_TAG'
),
string(
name: 'APP_VERSION',
description: '',
defaultValue: '',
trim: true
),
booleanParam(
name: 'RUNACTION',
description: '',
defaultValue: false
)
])
])
}
:!: multiline
// Так не работает, переменные не раскрываются
script{
echo ''' second $MY_PARAM '''
echo ''' three ${MY_PARAM} '''
echo ''' four ${params.MY_PARAM} '''
}
// Работает в тройных двойных кавычках, раскрылись все переменные
script{
echo """ second $MY_PARAM """
echo """ three ${MY_PARAM} """
echo """ four ${params.MY_PARAM} """
}
===== Дата/Время =====
* **java.time.LocalDateTime** - Дата и время без часового пояса в календарной системе ISO-8601, например 2007-12-03T10:15:30\\ неизменяемый объект даты и времени, с точностью до наносекунды, не хранит и не представляет часовой пояс\\
* **java.time.Instant** - тут что то более жесткое
* **ZoneId** - (java.time.ZoneId) больше связано с правилами преобразования между часовыми поясами\\
* **ZoneOffset** - (java.time.ZoneOffset) тут уже больше про смещение относительно UTC\\
:!: more
import java.time.LocalDateTime
// Локальная ДТ, в формате "2024-02-01T19:34:11.905900287"
def start = LocalDateTime.now()
// Прибавить минуты
def start = LocalDateTime.now().plusMinutes(360)
// Тоже текущая дата, формат такой же, но дженкинс попросил аппрув (хотя не помню с первым возможно тоже был)
import java.time.Instant
def current_instant = Instant.now()
:!: Часовые пояса
[[https://javarush.com/quests/lectures/questsyntaxpro.level16.lecture06|Неплохая статья]]\\
import java.time.LocalDateTime
import java.time.ZoneId
// ZoneId, тут есть явное определение временных зон, пример из джавы
ZoneId zone = ZoneId.of("Asia/Almaty")
ZonedDateTime time = ZonedDateTime.now(zone)
// В груви так работает (тут есть перечень зон https://csharpcoderr.com/5267/ )
def zoned_dt = LocalDateTime.now(ZoneId.of("Asia/Almaty"))
// Offset, собсна "смещение", тут в основном фиксированный какой то сдвиг времени от полученного из системы
// Так работает
def current_dt = LocalDateTime.now()
def offset_dt = LocalDateTime.now(ZoneOffset.ofHours(+6))
// Вот тоже пример, по сути тоже самое
ZoneOffset zoneOffSet = ZoneOffset.of("+02:00");
OffsetTime time = OffsetTime.now(zoneOffSet);
===== Примеры =====
:!: Работа с grafana API
import java.time.LocalDateTime
import java.time.ZoneId
pipeline {
agent {
label 'master'
}
environment {
GRAFANA_API_TOKEN = credentials('sdcsdcdscdsc')
}
stages {
stage('make silence') {
steps {
SetParameters()
script{
//def start_time_silence = LocalDateTime.now(ZoneId.of("Asia/Almaty"))
//def end_time_silence = start_time_silence.plusMinutes(params.DURATION_MIN.toInteger())
def start_time_silence = LocalDateTime.now()
def end_time_silence = start_time_silence.plusMinutes(params.DURATION_MIN.toInteger())
def response = httpRequest acceptType: 'APPLICATION_JSON',
contentType: 'APPLICATION_JSON',
responseHandle: 'NONE',
httpMode: 'POST',
ignoreSslErrors: true,
url: 'https://url-grafana/api/alertmanager/grafana/api/v2/silences',
wrapAsMultipart: false,
authentication: 'dcdcdcdcdc',
requestBody: """{
"comment": "comment_string",
"createdBy": "user_my",
"startsAt": "${start_time_silence}",
"endsAt": "${end_time_silence}",
"matchers": [
{
"isEqual": true,
"isRegex": false,
"name": "alertname",
"value": "${ALERT_NAME}"
}
]
}"""
}
}
}
}
}
def SetParameters() {
properties([
parameters([
string(
name: 'ALERT_NAME',
description: 'Название alert-правила в grafana',
trim: true
),
string(
name: 'DURATION_MIN',
defaultValue: '60',
description: 'Продолжительность режима тишины в минутах',
trim: true
)
])
])
}
:!: Минимальный пример, скачивание репозитория
pipeline {
agent {
label 'main-node'
}
options {
quietPeriod 5
ansiColor('xterm')
timestamps()
buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', numToKeepStr: '10')
}
stages {
stage('Prepare') {
steps {
script {
cleanWs()
sh(
label: 'repo sync',
script: '''
PATH="${HOME}/.bin:${PATH}"
repo init -u ssh://username:port/myproject --depth=1
repo sync
repo forall -c git checkout "master"
'''
)
}
}
}
}
}
#
# Выбор ветки
#
stage('Prepare') {
steps {
script {
SetParameters()
cleanWs()
sh(
label: 'repo sync',
script: '''
PATH="${HOME}/.bin:${PATH}"
repo init -u ssh://username:port/project --depth=1
repo sync
if [ ${BRANCH} != "master" ] ; then
repo forall -c git checkout ${BRANCH}
fi
if [ ${GERRIT_REFSPEC} != "master" ] ; then
cd igs/smodls/i_adm && git fetch ssh://url-repo:port/myproject ${GERRIT_REFSPEC} && git checkout FETCH_HEAD
fi
'''
)
}
}
}
:!: Проверка и откат
- name: "Проверка и откат"
block:
- name: "Перезапускаем сервис"
import_tasks: 60-restart_service.yml
- name: "Делаем паузу и проверяем состояние сервиса"
ansible.builtin.shell:
cmd: "sleep 25 && systemctl is-active myservice@{{ instance_name }}.service"
rescue:
- name: "Откатываемся на бекап"
ansible.builtin.shell:
cmd: 'tar -xzvf ./backups/previous_version.tar.gz -C .'
chdir: '{{ instance.root_path }}/{{ instance_name }}'
- name: "Перезапускаем сервис, уже после отката"
import_tasks: 60-restart_service.yml
- name: "Прерываем выполнение в случае отката"
ansible.builtin.fail:
msg: "!!! Откат изменений после сбоя. Прерываем дальнейшее выполнение !!!"
:!: Выбор инстансов
pipeline {
agent {
label 'ConfigServerExt'
}
options {
quietPeriod 5
ansiColor('xterm')
timestamps()
buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', numToKeepStr: '10')
}
environment {
EXTRAS = getExtras()
}
stages {
stage('Prepare') {
steps {
script {
SetParameters()
cleanWs()
script {
currentBuild.displayName = "${params.VERSION}"
buildDescription "${params.APP_INSTANCE}"
}
sh(
label: 'repo sync',
script: '''
PATH="${HOME}/.bin:${PATH}"
repo init -u ssh://jenkins@gerrit.ru/igs-meta --depth=1
repo sync
if [ ${BRANCH} != "master" ] ; then
repo forall -c git checkout ${BRANCH}
fi
if [ ${GERRIT_REFSPEC} != "master" ] ; then
cd igs/modules/repo && git fetch ssh://gerrit.ru/proj ${GERRIT_REFSPEC} && git checkout FETCH_HEAD
fi
'''
)
}
}
}
stage('Build') {
when {
expression {
!params.SKIP_BUILD
}
}
environment {
NEXUS_CREDS = credentials('11111-1111-1111')
}
steps {
sh label: 'gradle publish', script: '''
cd $WORKSPACE/igas
./gradlew \
"-PnexusUrl=https://nexus.ru/repository" \
"-PnexusUser=${NEXUS_CREDS_USR}" \
"-PnexusPassword=${NEXUS_CREDS_PSW}" \
:my_app:publish
'''
}
}
stage('Deploy') {
steps {
dir("${WORKSPACE}/proj/pathToPlaybook") {
ansiblePlaybook(
playbook: "playbooks-deploy/my_role.yml",
extras: "${env.EXTRAS}",
colorized: true,
)
}
}
}
}
}
def getExtras() {
if ("all".equals(params.APP_INSTANCE)) {
return "-e version=${params.VERSION}"
} else {
return "-e version=${params.VERSION} -e instances=${params.APP_INSTANCE}"
}
}
def SetParameters() {
properties([
parameters([
booleanParam(
name: 'SKIP_BUILD',
description: 'Поставьте галочку и выберите версию если нужен только деплой существующей версии',
defaultValue: false
),
[
$class: 'ChoiceParameter',
choiceType: 'PT_SINGLE_SELECT',
description: 'Ветка для сборки приложения',
name: 'BRANCH',
randomName: 'choice-parameter-73664823423423',
filterable: false,
script: [
$class: 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox: false,
script:
'''
return['Could not get branches list from gerrit']
'''.trim()
],
script: [
classpath: [],
sandbox: false,
script:
'''
def cmd = ['/bin/bash', '-c', 'git ls-remote --quiet --heads ssh://login@gerrit.ru/proj | grep -oP refs.* | sed "s|refs/heads|origin|g; s|master|master:selected|"']
def result = cmd.execute().text.tokenize()
return result
'''.trim()
]
]
],
[
$class: 'ChoiceParameter',
choiceType: 'PT_SINGLE_SELECT',
name: 'VERSION',
description: 'Укажите, если нужен деплой уже существующей версии',
randomName: 'choice-parameter-5631314456178618',
script: [
$class: 'GroovyScript',
script: [
classpath: [],
sandbox: false,
script: '''\
import com.cloudbees.plugins.credentials.CredentialsMatchers
import com.cloudbees.plugins.credentials.CredentialsProvider
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials
import com.cloudbees.plugins.credentials.domains.DomainRequirement
import jenkins.model.Jenkins
import hudson.security.ACL
jenkins = Jenkins.get()
def lookupSystemCredentials = {
credentialsId -> return CredentialsMatchers.firstOrNull(
CredentialsProvider.lookupCredentials(
StandardUsernameCredentials.class,
jenkins,
ACL.SYSTEM,
Collections.emptyList()
),
CredentialsMatchers.withId(credentialsId)
)
}
credential = lookupSystemCredentials("111111-1111-111")
nexusLogin = credential.getUsername()
nexusPasswd = credential.getPassword().getPlainText()
def targetUrl="https://$nexusLogin:$nexusPasswd@nexus.url.ru/repository/myapp-snapshots/com/gmware/applications/myapp/maven-metadata.xml"
def sout = new StringBuilder(), serr = new StringBuilder()
def proc = "curl -X GET $targetUrl".execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
def response=sout.toString()
def metadata = new XmlParser().parseText(response)
def versions = ['latest:selected']
versions.addAll(metadata.versioning.versions.version.collect({it.text()}).reverse())
return versions
'''.stripIndent()
]
]
],
[
$class : 'ChoiceParameter',
choiceType : 'PT_MULTI_SELECT',
filterLength: 1,
filterable : false,
name : 'FM_INSTANCE',
randomName : 'choice-parameter-5631314339613980',
script : [
$class: 'GroovyScript',
script: [
classpath: [],
sandbox : false,
script : '''\
def fm_instances = []
def file1 = new File("/pathToFile/file.txt")
file1.eachLine {
fm_instances.add(it)
}
def result = ["all:selected"]
result.addAll(fm_instances)
return result'''.stripIndent()
]
]
],
string(
defaultValue: 'master',
description: 'Укажите патчсет, например, refs/changes/12/34567/1, откуда забирать плейбуки ansible',
name: 'GERRIT_REFSPEC',
trim: true
)
])
])
}
:!:
====== Use Gradle ======
:!: Общее
:!: Скрипты справа все таки совпадают с тасками что есть в файле "build.gradle.kts" внутри проекта\\
вот только как он обновляется ? таску вроде прописал, а справа не появляется этого
// Проверка переданной переменной, через аргументы вызова, "-Pversion"
if(!project.hasProperty("version")) throw Exception("no value stop it")
// Обращаться к свойству
project.properties["buildVersion"]
// Вообще таска для ShadowJar
tasks {
"shadowJar"(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) {
mergeServiceFiles()
isZip64 = true
val serverAppName = "server_app-$serverAppVersion.jar"
archiveFileName.set(serverAppName)
doFirst {
//assert(buildVersion != "non-value") { "Can not get version by git." }
if(!project.hasProperty("version")) throw Exception("no value stop it")
}
}
}
:!: Работа из пайплайна
# Если скритп "gradlew" в папке с проектом
stage('Build jar') {
steps {
sh "bash ./gradlew shadowJar"
}
}
# Если нет, тогда
stage('Build jar') {
steps {
sh "bash ./gradlew myProject:modules:shadowJar" (хотя не точно уже)
}
}