====== 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" (хотя не точно уже) } }