Jenkinsfile Pipeline errors: “expected a step” and “undefined section” - jenkins

Can anyone explain why I get the following errors, and what can be a possible solution for them?
Running in Durability level: MAX_SURVIVABILITY
org.codehaus.groovy.control.MultipleCompilationErrorsException:
startup failed: WorkflowScript: 43: Expected a step # line 43, column
17.
if (isUnix()) {
^
WorkflowScript: 71: Undefined section "success" # line 71, column 5.
success {
^
WorkflowScript: 78: Undefined section "failure" # line 78, column 5.
failure {
^
WorkflowScript: 16: Tool type "maven" does not have an install of
"MAVEN_HOME" configured - did you mean "Maven M2"? # line 16, column
19.
maven "MAVEN_HOME"
^
WorkflowScript: 17: Tool type "jdk" does not have an install of
"JAVA_HOME" configured - did you mean "null"? # line 17, column 17.
jdk "JAVA_HOME"
^
The code in the Jenkinsfile is as follows:
pipeline {
// agent defines where the pipeline will run.
agent {
// Here we define that we wish to run on the agent with the label SL202_win
label "SL202_win"
}
// The tools directive allows you to automatically install tools configured in
// Jenkins - note that it doesn't work inside Docker containers currently.
tools {
// Here we have pairs of tool symbols (not all tools have symbols, so if you
// try to use one from a plugin you've got installed and get an error and the
// tool isn't listed in the possible values, open a JIRA against that tool!)
// and installations configured in your Jenkins master's tools configuration.
maven "MAVEN_HOME"
jdk "JAVA_HOME"
}
environment {
// Environment variable identifiers need to be both valid bash variable
// identifiers and valid Groovy variable identifiers. If you use an invalid
// identifier, you'll get an error at validation time.
// Right now, you can't do more complicated Groovy expressions or nesting of
// other env vars in environment variable values, but that will be possible
// when https://issues.jenkins-ci.org/browse/JENKINS-41748 is merged and
// released.
mvnHome = "D:/Tools/apache-maven-3.5.2"
}
stages {
// At least one stage is required.
stage("Preparation") {
// Every stage must have a steps block containing at least one step.
steps {
// Get some code from a GitHub repository
git 'https://git.ceesiesdomain.nl/scm/rsd/test_automation.git'
}
}
stage('Build') {
steps {
// Run the maven build
if (isUnix()) {
sh "'${mvnHome}/bin/mvn' clean test -Dtest=TestRunner"
} else {
bat(/"${mvnHome}\bin\mvn" clean test -Dtest=TestRunner/)
}
}
}
stage('Results') {
steps {
cucumber buildStatus: 'UNSTABLE', failedFeaturesNumber: 999, failedScenariosNumber: 999, failedStepsNumber: 3, fileIncludePattern: '**/*.json', skippedStepsNumber: 999
}
}
}
// Post can be used both on individual stages and for the entire build.
post {
success {
echo "Test run completed succesfully."
}
failure {
echo "Test run failed."
}
always {
// Let's wipe out the workspace before we finish!
deleteDir()
echo "Workspace cleaned"
}
}
success {
mail(from: "jenkins#ceesiesdomain.nl",
to: "ceesie#ceesiesdomain.nl",
subject: "That build passed.",
body: "Nothing to see here")
}
failure {
mail(from: "jenkins#ceesiesdomain.nl",
to: "ceesie#ceesiesdomain.nl",
subject: "That build failed!",
body: "Nothing to see here")
}
// The options directive is for configuration that applies to the whole job.
options {
// For example, we'd like to make sure we only keep 10 builds at a time, so
// we don't fill up our storage!
buildDiscarder(logRotator(numToKeepStr:'10'))
// And we'd really like to be sure that this build doesn't hang forever, so
// let's time it out after an hour.
timeout(time: 60, unit: 'MINUTES')
}
}

I managed to get it working by trimming back all excess and starting with the pure essentials and iteratively adding steps and configs and running it after each change to verify the workings.
The script I ended up with now is:
pipeline {
agent {
label 'SL202_win'
}
stages {
stage("Fetch repository") {
steps {
git 'https://git.ceesiesdomain.nl/scm/rsd/test_automation.git'
}
}
stage('Run test') {
steps {
bat 'cd d:/SL202_Data/workspace/Front-end-SwiftNL/Sanctie_Regressie_Workflows_WCM'
bat 'mvn clean test -f d:/SL202_Data/workspace/Front-end-SwiftNL/Sanctie_Regressie_Workflows_WCM/pom.xml -Dtest=TestRunner'
}
}
}
post {
always {
echo 'Test run completed'
cucumber buildStatus: 'UNSTABLE', failedFeaturesNumber: 999, failedScenariosNumber: 999, failedStepsNumber: 3, fileIncludePattern: '**/*.json', skippedStepsNumber: 999
}
success {
echo 'Successfully!'
}
failure {
echo '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'
}
}
options {
timeout(time: 60, unit: 'MINUTES')
}
}

My way is to put the if or for block in a script {} block or dir("") {} block when using declarative pipelines.

Related

Jenkins pipeline - skip next stage on conditional failure of pylint

I've a Jenkinsfile, which has a two different stages: Pre-Build and Build. The Pre-Build is executing pylint and uses the warnings-ng-plugin to report that back to Jenkins.
Something like that:
stages {
stage('Pre-build') {
steps {
script {
sh """#!/usr/bin/env bash
pip install .
pylint --exit-zero --output-format=parseable --reports=n myProject > reports/pylint.log
"""
}
}
post {
always {
recordIssues(
enabledForFailure: true,
tool: pyLint(pattern: '**/pylint.log'),
unstableTotalAll: 20,
failedTotalAll: 30,
)
}
failure {
cleanWs()
}
}
}
stage('Build') {
steps {
script {
sh """#!/usr/bin/env bash
set -e
echo 'I AM STAGE TWO AND I SHOULD NOT BE EXECUTED'
"""
}
}
post {
always {
cleanWs()
}
}
}
}
I'm running into a couple of issues here. Currently I'm setting pylint to --exit-zero, as I want the warnings-ng plugin decide if it is good to go or not, based on the report.
Currently this is set to fail at a total of 30 issues. Now, myProject has 45 issues and I want to prevent that the next stage, Build is entered. But currently I can't seem to be able to prevent this behaviour, as it always continuous to the Build stage.
The build is flagged as failure, because of the results determined within recordIssues, but it doesn't abort the job.
I've found a ticket on https://issues.jenkins-ci.org (Ticket), but I can't seem to make sense out of all of this.
I've found a solution to your problem and I think that this is a bug in the pipeline workflow. The warnings-ng correctly sets build status to failed, but next stages are started despite the status in ${currentBuild.currentResult} variable.
You can use when { expression { return currentBuild.currentResult == "SUCCESS" } } to skip later stages, or throwing an error. But I think this should be default behaviour. Your file then should like:
stages {
stage('Pre-build') {
steps {
script {
sh """#!/usr/bin/env bash
pip install .
pylint --exit-zero --output-format=parseable --reports=n myProject > reports/pylint.log
"""
}
}
post {
always {
recordIssues(
enabledForFailure: true,
tool: pyLint(pattern: '**/pylint.log'),
unstableTotalAll: 20,
failedTotalAll: 30,
)
}
}
}
stage('Build') {
when { expression { return currentBuild.currentResult == "SUCCESS" } }
steps {
script {
echo "currentResult: ${currentBuild.currentResult}"
sh """#!/usr/bin/env bash
set -e
echo 'I AM STAGE TWO AND I SHOULD NOT BE EXECUTED'
"""
}
}
}
post {
always {
cleanWs()
}
}
}
I've created an issue in their Jira.
My environment:
Jenkins ver.: 2.222.1
warnings-ng ver.: 8.1
worfklow-api ver.: 2.40
You have used post 2 times which is wrong implementation as post is designed to get executed only once after all stages are done. It should be written after all the stages just before end of pipeline.
To stop or skip the execution of 2nd Build stage, you can create global varaible at the top, capture the output of pylint in that and use if or when condition at start of stage. Something similar to --
pipeline {
def result
stages {
stage('Pre-build') {
steps {
script {
sh """#!/usr/bin/env bash
pip install .
pylint --exit-zero --output-format=parseable --reports=n myProject > reports/pylint.log
"""
}
}
}
}
stage('Pylint result') { // Not sure how recordIssue works. This just an example.
result = recordIssues(
enabledForFailure: true,
tool: pyLint(pattern: '**/pylint.log'),
unstableTotalAll: 20,
failedTotalAll: 30,
)
}
stage('Build') {
if ( result == "pass") {
steps {
script {
sh """#!/usr/bin/env bash
set -e
echo 'I AM STAGE TWO AND I SHOULD NOT BE EXECUTED'
"""
}
}
}
}
}
post { // this should be used after stages
always {
cleanWs()
}
failure {
cleanWs()
}
}
Also, stages are designed in such a way that if they fail, next stage will not be executed so it's a good idea to have the pylint to be executed inside a stage instead of post condition.
Note: The code above is just an example. Please modify it according to your need.
One option that you may consider is to fail the build explicitly with the following code:
post {
always {
recordIssues(
enabledForFailure: true,
tool: pyLint(pattern: '**/pylint.log'),
unstableTotalAll: 20,
failedTotalAll: 30
)
script {
if (currentBuild.currentResult == 'FAILURE') {
error('Ensure that the build fails if the quality gates fail')
}
}
}
}
Here, after you record the issues, you also check if the value of currentBuild.currentResult is FAILURE and in that case you explicitly call the error() function which fails the build correctly.

Step expected in Jenkins Groovy script

I am having below groovy script for my Jenkins pipeline. But when running its giving error as step expected where my script already having step. Can anyone suggest what's wrong here..
Script file
pipeline {
agent any
stages {
stage('Workspace Preparation') {
steps {
sh """
rm -rf ${workspace}/*
"""
}
}
stage('Get Deployment files') {
steps {
dir("${workspace}/deployfiles") {
if("${params.componentType}"=="A") {
echo "A component deployment"
checkout(## necessary step)
}
else if ("${params.componentType}"=="B") {
echo "B component deployment"
checkout(## necessary step)
}
else {
echo "Invalid"
}
}
}
}
}
}
Getting error as
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 19: Expected a step # line 14, column 6.
if("${params.componentType}"=="A") {
^
enter code here
enter code here
You are missing a script-block.
(Source)
Such a block gives you acces to execute groovy code (for, if-else etc. etc.)
stage('Check') {
steps {
script { // Allows to execute groovy code
dir (...) {
if (...)
}
}
}
See also: How to fix Pipeline-Script “Expected a step” error

Jenkins does not accept variable declaration

I am trying to start a docker image from Jenkins.
(Not getting Docker to run from within Jenkins)
I think I'm really close but this part has still some issues.
Can please anyone help?
stage('build Dockerimage 1') {
steps{
apitestimage = docker.build('apitestimage', '--no-cache=true dockerbuild')
}
}
stage('start Dockerimage and Tests 2') {
steps{
apitestimage.inside {
sh 'cd testing && ctest'
}
}
}
Jenkins reports:
WorkflowScript: 21: Expected a step # line 21, column 15. apitestimage = docker.build('apitestimage', '--no-cache=true dockerbuild')
and also
WorkflowScript: 27: Method calls on objects not allowed outside "script" blocks. # line 27, column 13. apitestimage.inside {
From your error, it shows that you're missing a script block in your steps. You'll need a script block when using the DSL in steps.
stage('build Dockerimage 1') {
steps{
script {
def apitestimage = docker.build('apitestimage', '--no-cache=true dockerbuild')
}
}
}
stage('start Dockerimage and Tests 2') {
steps{
script {
apitestimage.inside {
sh 'cd testing && ctest'
}
}
}
}
References:
https://jenkins.io/doc/book/pipeline/syntax/#script
In my case, I did declare a variable using the stages name: def stages = [buildStage]
Changing this variable name to another name fixed my issue.

Set the build name and description from a Jenkins Declarative Pipeline

I would like to set the build name and description from a Jenkins Declarative Pipeline, but can't find the proper way of doing it. I tried using an environment bracket after the pipeline, using a node bracket in an agent bracket, etc. I always get syntax error.
The last version of my Jenkinsfile goes like so:
pipeline {
stages {
stage("Build") {
steps {
echo "Building application..."
bat "%ANT_HOME%/bin/ant.bat clean compile"
currentBuild.name = "MY_VERSION_NUMBER"
currentBuild.description = "MY_PROJECT MY_VERSION_NUMBER"
}
}
stage("Unit Tests") {
steps {
echo "Testing (JUnit)..."
echo "Testing (pitest)..."
bat "%ANT_HOME%/bin/ant.bat run-unit-tests"
}
}
stage("Functional Test") {
steps {
echo "Selenium..."
}
}
stage("Performance Test") {
steps {
echo "JMeter.."
}
}
stage("Quality Analysis") {
steps {
echo "Running SonarQube..."
bat "%ANT_HOME%/bin/ant.bat run-sonarqube-analysis"
}
}
stage("Security Assessment") {
steps {
echo "ZAP..."
}
}
stage("Approval") {
steps {
echo "Approval by a CS03"
}
}
stage("Deploy") {
steps {
echo "Deploying..."
}
}
}
post {
always {
junit '/test/reports/*.xml'
}
failure {
emailext attachLog: true, body: '', compressLog: true, recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'DevelopersRecipientProvider']], subject: '[JENKINS] MY_PROJECT build failed', to: '...recipients...'
}
success {
emailext attachLog: false, body: '', compressLog: false, recipientProviders: [[$class: 'DevelopersRecipientProvider']], subject: '[JENKINS] MY_PROJECT build succeeded', to: '...recipients...'
}
}
}
Error is:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 11: Expected a step # line 11, column 5.
currentBuild.name = "MY_VERSION_NUMBER"
^
WorkflowScript: 12: Expected a step # line 12, column 5.
currentBuild.description = "MY_PROJECT MY_VERSION_NUMBER"
^
Ideally, I'd like to be able to read MY_PROJECT and MY_VERSION_NUMBER from the build.properties file, or from the Jenkins build log. Any guidance about that requirement would be appreciated as well.
UPDATE
Based on the answer I had below, the following worked:
stage("Build") {
steps {
echo "Building application..."
bat "%ANT_HOME%/bin/ant.bat clean compile"
script {
def props = readProperties file: 'build.properties'
currentBuild.displayName = "v" + props['application.version']
}
}
Now the build version is automatically set during the pipeline by reading the build.properties file.
I think this will do what you want. I was able to do it inside a script block:
pipeline {
stages {
stage("Build"){
steps {
script {
currentBuild.displayName = "The name."
currentBuild.description = "The best description."
}
... do whatever.
}
}
}
}
The script is kind of an escape hatch to get out of a declarative pipeline. There is probably a declarative way to do it but i couldn't find it. And one more note. I think you want currentBuild.displayName instead of currentBuild.name In the documentation for Jenkins globals I didn't see a name property under currentBuild.
If you want to set build name to a job from a parameter, you can use
currentBuild.displayName = "${nameOfYourParameter}".
Make sure you use double quotes instead of single quotes.
Job Configuration
Build job with parameter
Build History
REFERENCE: How to set build name in Pipeline job?

Jenkinsfile Pipeline errors: "expected a symbol" and "undefined section"

Can anyone explain why I get the following errors, and what can be a possible solution for them?
org.codehaus.groovy.control.MultipleCompilationErrorsException:
startup failed: WorkflowScript: 20: Expected a symbol # line 20,
column 4.
environment {
WorkflowScript: 17: Undefined section "error" # line 17, column 1.
pipeline {
The code in the Jenkinsfile is as follows:
#!groovy
def application, manifest, git, environment, artifactory, sonar
fileLoader.withGit('git#<reducted>', 'v1', 'ssh-key-credential-id-number') {
application = fileLoader.load('<reducted>');
manifest = fileLoader.load('<reducted>');
git = fileLoader.load('<reducted>');
environment = fileLoader.load('<reducted>');
}
pipeline {
agent { label 'cf_slave' }
environment {
def projectName = null
def githubOrg = null
def gitCommit = null
}
options {
skipDefaultCheckout()
}
stages {
stage ("Checkout SCM") {
steps {
checkout scm
script {
projectName = git.getGitRepositoryName()
githubOrg = git.getGitOrgName()
gitCommit = manifest.getGitCommit()
}
}
}
stage ("Unit tests") {
steps {
sh "./node_modules/.bin/mocha --reporter mocha-junit-reporter --reporter-options mochaFile=./testResults/results.xml"
junit allowEmptyResults: true, testResults: 'testResults/results.xml'
}
}
//stage ("SonarQube analysis") {
//...
//}
// stage("Simple deploy") {
// steps {
// // Login
// sh "cf api <reducted>"
// sh "cf login -u <reducted> -p <....>"
//
// // Deploy
// sh "cf push"
// }
// }
}
post {
// always {
// }
success {
sh "echo 'Pipeline reached the finish line!'"
// Notify in Slack
slackSend color: 'yellow', message: "Pipeline operation completed successfully. Check <reducted>"
}
failure {
sh "echo 'Pipeline failed'"
// Notify in Slack
slackSend color: 'red', message: "Pipeline operation failed!"
//Clean the execution workspace
//deleteDir()
}
unstable {
sh "echo 'Pipeline unstable :-('"
}
// changed {
// sh "echo 'Pipeline was previously failing but is now successful.'"
// }
}
}
Your Pipeline is mostly fine — adding Scripted Pipeline elements before the Declarative pipeline block is generally not a problem.
However, at the very start, you're defining an variable called environment (and git), which are basically overriding the elements declared by the various Pipeline plugins.
i.e. When you attempt to do pipeline { environment { … } }, the environment is referring to your variable declaration, which causes things to go wrong.
Rename those two variables, and you'll fix the first error message.
To fix the second error message, remove the attempts to declare variables from the environment block — this block is only intended for exporting environment variables for use during the build steps, e.g.:
environment {
FOO = 'bar'
BAR = 'baz'
}
The script block you have will work fine without these declarations. Alternatively, you can move those variable declarations to the top level of your script.
If you're using declarative pipeline (which you are, e.g. the outer pipeline step), then you may only declare the pipeline on the outer layer, e.g. you can't have variable and function definitions. This is the downside of using declarative pipeline.
More info here
As I see it you can solve this the following ways:
Use scripted pipeline instead
Move the code at the beginning to a global pipeline library (Might be tricky to solve variable scoping if a value is used in several places, but it should be doable.
Move the code at the beginning to an script step inside the pipeline and store the values as described here.

Resources