Jenkinsfile post always directive with multiple steps - jenkins

I am wondering if it's possible to use various steps block on inside a post step.
Here's the actual code:
pipeline {
agent any
stages {
stage('Test') {
steps {
sh 'bash testing.sh'
}
}
}
post {
always {
steps {
sh 'bash cleaning-procedure-1.sh'
}
steps {
sh 'bash cleaning-procedure-2.sh'
}
steps {
sh 'bash general-cleaning.sh'
}
}
}
}
One of the errors that Jenkins gets:
WorkflowScript: 291: Missing required parameter: "delegate" # line 291, column 13.
step {
Is it possible to create different steps inside a POST - ALWAYS block on Jenkins?

steps blocks are not allowed inside a post directive. If you want to use the sh method, then you can invoke it directly outside of the steps scope:
post {
always {
sh 'bash cleaning-procedure-1.sh'
sh 'bash cleaning-procedure-2.sh'
sh 'bash general-cleaning.sh'
}
}

You can use a script block to have multiple actions, like
post {
always {
script {
junit '**/build/junit.xml'
xunit (tools: [CTest(pattern: '**/build/ctest/**/*.xml')] ...)
}
}
}

Related

Set environment variable from Jenkins Pipeline

I'd like to set an env variable in one Stage and have it available in all subsequent Stages and Steps. Something like this:
pipeline {
stages {
stage('One') {
steps {
sh 'export MY_NAME=$(whoami)'
}
}
stage('Two') {
steps {
sh 'echo "I am ${MY_NAME}"'
}
}
stage('Three') {
steps {
sh 'echo "I am ${MY_NAME}"'
}
}
}
}
Those sh steps seem to be independent of each other, and the exported var is not preserved even for the next Step, let alone Stage.
One way I can think of is to write the var to a shell file, like echo "FOLDER_CONTENT=$(ls -lh)" and then source it a next Step, but again, I'll have to do the sourcing in every next Step, which is suboptimal.
Is there a better way to do that?
Finally was able to achieve it like so:
pipeline {
stages {
stage('One') {
steps {
script {
env.MY_NAME= sh (
script: 'whoami',
returnStdout: true
).trim()
}
}
}
stage('Two') {
steps {
echo "I am ${MY_NAME}"
}
}
stage('Three') {
steps {
sh 'echo "I am ${MY_NAME}"'
}
}
}
}

How do I run the same stages on multiple nodes in Jenkins declarative pipeline?

We have a pipeline like this:
pipeline {
agent none
stages {
stage('Build') {
// ...
}
stage('Test') {
parallel {
stage('Test on Debian') {
agent {
label 'debian'
}
steps {
unstash 'compile-artifacts'
unstash 'dot-gradle'
sh './gradlew check --stacktrace'
}
post {
always {
junit '*/build/test-results/**/*.xml'
}
}
}
stage('Test on CentOS') {
agent {
label 'centos'
}
steps {
unstash 'compile-artifacts'
unstash 'dot-gradle'
sh './gradlew check --stacktrace'
}
post {
always {
junit '*/build/test-results/**/*.xml'
}
}
}
stage('Test on Windows') {
agent {
label 'windows'
}
steps {
unstash 'compile-artifacts'
unstash 'dot-gradle'
bat "gradlew.bat check --stacktrace"
}
post {
always {
junit '*/build/test-results/**/*.xml'
}
}
}
stage('Test on macOS') {
agent {
label 'macos'
}
steps {
unstash 'compile-artifacts'
unstash 'dot-gradle'
sh './gradlew check --stacktrace'
}
post {
always {
junit '*/build/test-results/**/*.xml'
}
}
}
}
}
}
}
Every stage is essentially identical, save for one line in the Windows block which I already know how to deal with, so is there a way to template out the common parts of these stages to remove the duplication?
I already tried putting a loop inline, but it's not something that declarative pipelines let you do. :(
You can refactor your step{}-blocks with groovy-methods:
def stageX(boolean linux) {
unstash 'compile-artifacts'
unstash 'dot-gradle'
if (linux) {
sh './gradlew check --stacktrace' }
else {
bat "gradlew.bat check --stacktrace" }
}
which you have to call like the following in your step{}:
steps {
script { stageX( true) } // or with false for your windows agent
}
Of course you can do the same for your junit-plugin-call:
def junitCall() {
junit '*/build/test-results/**/*.xml'
}
and call it like:
post {
always {
script { junitCall()
}
}
}
You won't win a lot of lines but it will improve the handling of the code a lot. If you want to cleanup your Jenkinsfile even more you could put the methods into a shared-library which you import so they aren't even declared in your Jenkinsfile.
Essentially what you want to do is currently not possible. As https://jenkins.io/doc/book/pipeline/shared-libraries/#defining-declarative-pipelines states:
Only entire pipelines can be defined in shared libraries as of this
time. This can only be done in vars/*.groovy, and only in a call
method. Only one Declarative Pipeline can be executed in a single
build, and if you attempt to execute a second one, your build will
fail as a result.
So you can define methods to bundle several steps or you can bundle a whole pipeline in a shared library but nothing in between. Which is a shame, really.

Declarative Pipeline shared library

I'm facing an issue when trying to implement shared library in my Jenkins servers.
The error I'm getting is around the following
No such DSL method 'agent' found among steps
I have tried to remove the agent and just run on node, but still issue.
I was following the following: https://jenkins.io/blog/2017/09/25/declarative-1/
could someone please point out where I'm be going wrong
vars/jenkinsJob.groovy
def call() {
// Execute build pipeline job
build_pipeline()
}
def build_pipeline() {
agent {
node {
label params.SLAVE
}
}
parameters {
string(name: 'SETTINGS_CONFIG_FILE_NAME', defaultValue: 'maven.settings')
string(name: 'SLAVE', defaultValue: 'new_slave')
}
environment {
mvn = "docker run -it --rm --name my-maven-project -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven maven:3.3-jdk-8"
}
stages {
stage('Inject Settings.xml File') {
steps {
configFileProvider([configFile(fileId: "${env.SETTINGS_CONFIG_FILE_NAME}", targetLocation: "${env.WORKSPACE}")]) {
}
}
}
stage('Clean') {
steps {
sh "${mvn} clean"
}
}
stage('Lint') {
steps {
sh "${mvn} lint"
}
}
stage('Build package and execute tests') {
steps {
sh "${mvn} build"
}
}
}
post {
always {
archive "**/target/surefire-reports/*"
junit '**/target/surefire-reports/*.xml'
step([$class: 'JacocoPublisher'])
}
}
}
Jenkinsfile
#Library('pipeline-library-demo') _
jenkinsJob.call()
All valid Declarative Pipelines must be enclosed within a pipeline block
eg:
pipeline {
/* insert Declarative Pipeline here */
/* import libraries and call functions */
}
The file jenkinsJob.groovy needs to have a single method only by the name:
def call(Map params[:]){
// method body
}

Jenkins declarative pipeline can't find some scripts

I have written a Jenkins pipeline where the relevant parts looks as follows:
pipeline {
agent {
dockerfile true
}
triggers {
pollSCM('H 1 * * 1-5')
}
options {
buildDiscarder(logRotator(artifactNumToKeepStr: "${NUMBER_OF_ARTIFACTS_TO_KEEP}"))
disableConcurrentBuilds()
timeout(time: 60, unit: 'MINUTES')
timestamps()
}
stages {
stage('Metadata') {
steps {
script {
sh 'java -version'
}
script {
sh './mvnw -v'
}
}
}
stage('Build') {
steps {
script {
sh './mvnw --batch-mode clean install'
}
}
}
stage('Archive artifacts (develop/master)') {
when {
anyOf {
branch 'master'
branch 'develop'
}
}
steps {
script {
sh './package.sh'
}
archive '**/target/*.jar'
archiveArtifacts artifacts: '*.deb'
}
}
}
post {
always {
deleteDir()
}
failure {
sendNotifications currentBuild.result
}
unstable {
sendNotifications currentBuild.result
}
}
}
And my Dockerfile:
FROM alpine
RUN apk add --no-cache dpkg openjdk8
All scripts run fine, except package.sh where I get the following log in the output:
07:47:25 [chx-sync_-sync_master-A2F53LY4I2X54TLDEU2Z2PXI423NI6FODHQDS7CRIKCCNDF5UGOA] Running shell script
07:47:25 + ./package.sh
07:47:25 /home/jenkins/jenkins/workspace/chx-sync_-sync_master-A2F53LY4I2X54TLDEU2Z2PXI423NI6FODHQDS7CRIKCCNDF5UGOA#tmp/durable-dbcb4143/script.sh: line 1: ./package.sh: not found
I can't figure out why all scripts except this one would work. They are all located in the root of the project in Git. Is there some command in my pipeline that would change the working directory, or what is going on here?
EDIT:
I'm guessing the shebang in package.sh might be relevant? It is #!/bin/bash.
I got the answer from this answer on SO. I simply changed the using #!/bin/sh and it is working.

Chained multiple pipeline based on 'post' jenkins block

I'm beginner to Jenkins. I have code pipeline structure like this
Repo1 -> Repo2 -> Repo3 -> Deploy
I already created such hierarchy via GUI but I want to create it via pipeline as code.I want to create chain of pipelines where I clone different repos and perform tests on it and then continue to another repo based on current pipeline post result.
This is my jenkinsfile - (psuedo code like as it gives me error to build)
pipeline {
agent any
stages {
stage('Build Repo1') {
steps {
sh 'echo "repo1 build!"'
}
}
stage('Test Repo1') {
steps {
sh 'echo "repo success!"'
}
}
}
post {
success {
pipeline {
agent any
stages {
stage('Build Repo2') {
steps {
sh 'echo "build repo2!"'
}
}
stage('Test Repo2') {
steps {
sh 'echo "test repo2!"'
}
}
}
post {
success {
# continue to generate pipeline for repo3
echo 'This will always run'
}
failure {
echo 'This will run only if failed'
}
}
}
}
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'
}
}
}
Please help!

Resources