Jenkins pipeline regex not matching anything - jenkins

I've got a regex with sample text that is working on regex101, but doesn't seem to work in my Jenkins pipeline scenario. So I'm assuming I've got a mistake in my pipeline script, but I fail to see where.
Here's a repro:
pipeline {
agent any
stages {
stage ('Test') {
steps {
script {
echo ("Test")
output = "Some text. \n\n 12 scenarios (3 failed, 2 success) plus text \n\n and some more text"
def hasSummaryMatch = (output ==~ /\d+ scenarios \([^()]+\)/)
echo ("hasSummaryMatch = " + hasSummaryMatch)
if (!hasSummaryMatch) {
error ("No summary!")
}
}
}
}
}
}
I've tested this with Jenkins 2.60.2 running in the official Docker container.
This provides the following (abbreviated) output:
Started by user Administrator
Running on master in /var/jenkins_home/workspace/Test001
Test
hasSummaryMatch = false
ERROR: No summary!
Finished: FAILURE
The expected output is no error because there should be a match.
What am I doing wrong?

Just use =~ (the find operator) instead of ==~ (the match operator):
def hasSummaryMatch = (output =~ /\d+ scenarios \([^()]+\)/)
When match operator ==~ is used, then a strict match of the input string required

Related

"Ambiguous expression could be either a parameterless closure expression or an isolated open code block in jenkins parallel execution throws error

following code is throwing below error.
if(!SkipLanguageComponentTests){
^
WorkflowScript: : Groovy compilation error(s) in script. Error(s): "Ambiguous expression could be either a parameterless closure expression or an isolated open code block;
solution: Add an explicit closure parameter list,
script {
2 errors
`
def SkipLanguageComponentTests = false;
pipeline {
parameters {
booleanParam(name: 'SkipLanguageComponentTests', defaultValue: false, description: 'XYZ')
}
stages {
stage('Checkout Source') {
steps {
checkout scm
}
}
stage("Component & Language Tests"){
steps{
parallel (
"componentTestsTask":{
//component test start
dir("docker"){
sh script: "docker-compose -f blah blah\""
}
// some xyz step here
//component test ends here
},
"integrationTestTasks":{
// language test script starts
if(!SkipLanguageComponentTests){
//run lang test and publish report
} else {
echo "Skip Language Component Tests"
}
// language test script ends
}
)
}
}
}
`
I have tried as per the documentation https://www.jenkins.io/blog/2017/09/25/declarative-1/
I have tried this based on the answer mentioned in : Running stages in parallel with Jenkins workflow / pipeline
stage("Parallel") { steps { parallel ( "firstTask" : { //do some stuff }, "secondTask" : { // Do some other stuff in parallel } ) } }
Can someone help me to resolve this ?
Ok, here is the working version of your pipeline - with proper IF inside:
parameters {
booleanParam(name: 'SkipLanguageComponentTests', defaultValue: false, description: '')
}
agent { label 'master' }
stages {
stage("Component & Language Tests") {
parallel {
stage("componentTestsTask") {
steps {
//component test start
echo "docker"
// some xyz step here
//component test ends here
}
}
stage("integrationTestTasks") {
steps {
script {
// language test script starts
if (!params.SkipLanguageComponentTests) {
echo "not skipped"
//run lang test and publish report
} else {
echo "Skip Language Component Tests"
}
}
}
// language test script ends
}
}
}
}
}
This pipeline is not optimal, use below information to improve it.
Notes:
you are using declarative pipeline so I think it is better to stay with parallel section expressed in declarative way
help is here: Jenkins doc about parallel
there is scripted pipeline as well Jenkins doc about scripted pipeline
as I stated in original answer, you have to use params to refer to input parameter properly
if you are using code you have to enclose it in script section - this is like putting scripted pipeline inside declarative one
IF statement can also be done declaratively: Jenkins doc about WHEN
I recommend not to mix these 2 styles: so if you are using both for some good reason they should be separated from one another as much as possible for example by using function or library inside declarative pipeline - the goal is to have pipeline as clear and readable as possible
You are using input variable, so try to refer as it should be done for input in Jenkins:
if(!params.SkipLanguageComponentTests)

How can i stop a remote build in Jenkins if a parameter in the url is not valid?

I run remote builds in Jenkins as follows:
JENKINS_URL/job/JOBNAME/build?token=TOKEN
If i add an extra parameter on the query string as follows:
JENKINS_URL/job/JOBNAME/build?token=TOKEN&User=test#test.com&Key=Wxfder$324
As the first step in the build I want to extract these values ie token, User and Key and do some validation and if not valid , then stop the job.
Is there a Build Step i can use, how can i do this ?
One way to do this is by passing the data you need appending to the build cause. Refer to the following example.
The URL
Note the content assigned to cause= parameter
http://localhost:8080/job/Scripted/build?token=12345678&cause=User:test#test.com,Key:Wxfder$324
The Pipeline
pipeline {
agent any
stages {
stage('Test') {
steps {
script {
def cause = currentBuild.getBuildCauses()[0]
def note = cause.getString("note")
echo "${note}"
}
}
}
}
}
Above will give you the following Output.
[Pipeline] echo
User:test#test.com,Key:Wxfder$324

Jenkins declarative pipeline - How to abort whole build if certain conditions are not met instead of skipping a stage?

I am able to skip a stage using when condition successfully in jenkins declarative pipeline but I want to early abort the build if a set of conditions are not met. I tried putting when block at top level inside stages and outside stages as well but it gives syntax error saying "Expected stage" and "Undefined section when" respectively. Can anyone suggest how can I make it work ?
when {
anyOf {
not {
equals expected: true, actual: params.boolean_parameter
}
not{
equals expected: '', actual: params.string_parameter
}
}
}
In declarative pipelines the when directive can be used only on stages.
To solve your issue you can just create a dummy stage for aborting the pipeline in case the condition is not meant, in that step you can use a regular when directive and inside the steps of the stage just use the error keyword for aborting the build with a relevant message (see the error documentation).
Something like:
pipeline {
agent any
stages {
stage('Validate Conditions') {
when {
anyOf {
not {
equals expected: true, actual: params.boolean_parameter
}
not{
equals expected: '', actual: params.string_parameter
}
}
}
steps {
error("Aborting the build because conditions are not met")
}
}
... // rest of pipeline
}
}
This way if conditions are not met the build will be aborted, however the post section will still be executed allowing you to send notifications and so if needed.

How to set parallel step results in Jenkins Pipeline?

I'm running some tests with Jenkins Pipeline using parallelism and I noticed that Blue Ocean behaves weirdly with job results. I declared a global class with a variable to handle the current build status based on the parallel jobs results.
It works, however the parallel steps status does not seem to be getting set correctly within the Blue Ocean UI.
I prepared an example below to show what is going on. The pipeline definition is:
pipeline {
stages {
stage('Tests') {
steps {
script {
parallel(
a: {
echo "This is branch a"
manager.build.#result = hudson.model.Result.SUCCESS
},
b: {
echo "This is branch b"
manager.build.#result = hudson.model.Result.ABORTED
},
c: {
echo "This is branch c"
manager.build.#result = hudson.model.Result.UNSTABLE
},
d: {
echo "This is branch d"
manager.build.#result = hudson.model.Result.FAILURE
try {sh "exit 1"}catch(e){echo 'failure'}
}
)
manager.build.#result = hudson.model.Result.UNSTABLE
}
}
}
}
}
The last row changes the build result correctly (whatever value I give it - SUCCESS, FAILURE, UNSTABLE or ABORTED).
However, the result for each parallel step are always SUCCESS. The only exception is when I give the general build the status of UNSTABLE, then all parallel steps become UNSTABLE as well. Below follows an illustration:
I expected each parallel step to have it's own result, it would be very useful for debugging test results, since I need to run tests in different machines / devices in parallel.
Is it a Blue Ocean problem or am I doing something wrong here?

How to differentiate build triggers in Jenkins Pipeline

I'm hoping to add a conditional stage to my Jenkinsfile that runs depending on how the build was triggered. Currently we are set up such that builds are either triggered by:
changes to our git repo that are picked up on branch indexing
a user manually triggering the build using the 'build now' button in the UI.
Is there any way to run different pipeline steps depending on which of these actions triggered the build?
The following code should works to determine if a user has started the pipeline or a timer/other trigger:
def isStartedByUser = currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause) != null
In Jenkins Pipeline without currentBuild.rawBuild access the build causes could be retrieved in the following way:
// started by commit
currentBuild.getBuildCauses('jenkins.branch.BranchEventCause')
// started by timer
currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')
// started by user
currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
You can get a boolean value with:
isTriggeredByTimer = !currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause').isEmpty()
Or, as getBuildCauses() returns an array, the array's size will work correctly with Groovy truthy semantics:
if (currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')) {
The ability to get causes for a workflow run was released in version 2.22 (2018 Nov 02) to the Pipeline Supporting APIs Plugin. The feature was requested in JENKINS-41272.
A couple methods were added to the currentBuild global variable with that release:
getBuildCauses
Returns a JSON array of build causes for the current build
EXPERIMENTAL - MAY CHANGE getBuildCauses(String causeClass)
Takes a string representing the fully qualified Cause class and returns a JSON array of build causes filtered by that type for the current build, or an empty JSON array if no causes of the specified type apply to the current build
And an example from me submitting:
echo "${currentBuild.buildCauses}" // same as currentBuild.getBuildCauses()
echo "${currentBuild.getBuildCauses('hudson.model.Cause$UserCause')}"
echo "${currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')}"
And the output:
[Pipeline] echo
[[_class:hudson.model.Cause$UserIdCause, shortDescription:Started by user anonymous, userId:null, userName:anonymous], [_class:org.jenkinsci.plugins.workflow.cps.replay.ReplayCause, shortDescription:Replayed #12]]
[Pipeline] echo
[]
[Pipeline] echo
[]
[Pipeline] End of Pipeline
Finished: SUCCESS
NOTE
There appears to be an issue with the currentBuild.getBuildCauses(type) when the type is a type of Cause contributed by a plugin. For example, currentBuild.getBuildCauses('org.jenkinsci.plugins.workflow.cps.replay.ReplayCause') fails with a java.lang.ClassNotFoundException. This was reported in JENKINS-54673 for the 2.22 version of the Pipeline: Supporting APIs (workflow-support) plugin. It is reportedly fixed in the 2.24 version.
I might be missing something, but you can achieve what you want easily by making use of the when directive:
pipeline {
agent any
stages {
stage('Always') {
steps {
echo "I am always executed"
}
}
stage('ManualTimed') {
steps {
echo "I am only executed when triggered manually or timed"
}
when {
beforeAgent true
anyOf {
triggeredBy 'TimerTrigger'
triggeredBy cause: 'UserIdCause'
}
}
}
stage('GitLabWebHookCause') {
steps {
echo "I am only executed when triggered by SCM push"
}
when {
beforeAgent true
triggeredBy 'GitLabWebHookCause'
}
}
}
}
You will find many similar useful examples for various use cases in the documentation of the when directive.
Edit:
thanks to Jean-Francois Larvoire's answer, I was able to figure out 'my trigger' GitLabWebHookCause I required for my use case.
#vitalii-blagodir:
Your answer works for detecting builds triggered by users and timers, but not by commits.
Instead, I found this to work in my case:
def isTriggeredByIndexing = currentBuild.getBuildCauses('jenkins.branch.BranchIndexingCause').size()
def isTriggeredByCommit = currentBuild.getBuildCauses('com.cloudbees.jenkins.GitHubPushCause').size()
def isTriggeredByUser = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause').size()
def isTriggeredByTimer = currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause').size()
The .size() suffix returns 0 if the object is missing, or 1 if it's present. This makes the result usable as a boolean.
For finding the object name to use, I found it convenient to display this in the log:
echo "# Build causes"
def buildCauses = currentBuild.buildCauses
def numCause = 0
for (cause in buildCauses) {
echo "${numCause++}: ${cause.shortDescription}" // Display a human-readable index and description
echo "${cause}" // Display the object class name. This allows knowing what names to use in getBuildCauses(name) calls below.
}
Finally, if the goal is to abort a pipeline build in specific cases, then the test must be done before the beginning of the pipeline.
For example, we had a problem with the branch indexing triggering extra useless builds. This was fixed by adding this before the pipeline:
// Avoid useless buils: The branch indexing should only trigger the initial build of a new branch.
def isTriggeredByBranchIndexing = currentBuild.getBuildCauses('jenkins.branch.BranchIndexingCause').size()
if (isTriggeredByBranchIndexing && currentBuild.previousBuild) { // Then it's not the initial build.
echo "# Reindexing a branch already built. It is useless to rebuild it now. Aborting."
currentBuild.result = 'SUCCESS' // Make sure the build is not displayed in red in the Jenkins UI.
return // Abort before the pipeline even starts. (Inside the pipeline, this would only abort one stage.)
}
I think that the answers here are incomplete and do not provide an actual ready to use answer. Here's my code to get it working:
import com.cloudbees.groovy.cps.NonCPS
#NonCPS
def isStartedByTimer() {
def buildCauses = currentBuild.rawBuild.getCauses()
echo buildCauses
boolean isStartedByTimer = false
for (buildCause in buildCauses) {
if ("${buildCause}".contains("hudson.triggers.TimerTrigger\$TimerTriggerCause")) {
isStartedByTimer = true
}
}
echo isStartedByTimer
return isStartedByTimer
}
// [...]
// Other pipeline stuff
script {
isStartedByTimer()
}
When started by user:
00:00:01.353 [hudson.model.Cause$UserIdCause#fa5cb22a]
[Pipeline] echo
00:00:01.358 false
When started by timer:
00:00:01.585 [hudson.triggers.TimerTrigger$TimerTriggerCause#5]
[Pipeline] echo
00:00:01.590 true
Note: the NonCPS decorator is needed because otherwise the next non-script step will throw.
Assuming the two different build causes are "timer" and "push" (to a git repo), you can add the following stage to your Jenkinsfile (in a declarative Jenkins pipeline) to make use of getBuildCauses():
pipeline {
stages {
stage('preparation') {
steps {
script {
// get build cause (time triggered vs. SCM change)
def buildCause = currentBuild.getBuildCauses()[0].shortDescription
echo "Current build was caused by: ${buildCause}\n"
// e.g. "Current build was caused by: Started by GitHub push by mirekphd"
// vs. "Started by timer"
}
}
}
}
}
Then I can decide whether to perform certain stages conditionally (depending on the build cause). For example, pulling a docker base image and inspecting for changes in system libraries (likely security updates) should be done periodically, regardless of whether there was a source code change or not.
We can use "BUILD_CAUSE" variable for getting the information about who initiated the run
for [jenkins-pipeline] you may use
currentBuild.rawBuild.getCauses()
(see github.com/jenkinsci/pipeline-examples/blob/master/… for more details)
There was a similar requirement, where user detail who triggered the build should be there in success / failure notification. The job was already had time based triggered, hence could not use wrap([$class: 'BuildUser']) directly.
I used below step, which print username if the job is triggered manually or timer triggered. So, I used this:
pipeline {
agent any
stages {
stage('Test') {
steps {
script{
env.buildCauses = currentBuild.rawBuild.getCauses()
if (buildCauses.contains("hudson.triggers.TimerTrigger")){
env.builduser = "TimerTrigger"
} else {
wrap([$class: 'BuildUser']) {
env.builduser = "${BUILD_USER}"
}
}
}
echo "Initiated by: ${env.builduser}"
}
}
}
}

Resources