The problem is when the pipeline runs and passes I get "null" which I know "null" is a success. When a stage fails the notification properly shows "FAILURE"
Where should I be putting jenkins.currentBuild.result = CommonStrings.SUCCESS or what needs altered?
This is the method I utilize to make the curl:
def notifyTeam(String webhook2) {
jenkins.stage('Post Build Notify Teams') {
jenkins.echo "${jenkins.currentBuild.result}"
String jobName = jenkins.currentBuild.fullDisplayName
String status = jenkins.currentBuild.result
jenkins.echo "Job Name: ${jobName} STATUS: ${status}"
jenkins.sh "curl -H 'Content-Type: application/json' -d '{\"text\": \"Job Name: ${jobName} Status: ${status} URL: <${jenkins.env.BUILD_URL}>\"}' ${webhook2}"
jenkins.echo "${status}"
}
}
This is the closure pipeline:
try {
chat.prebuildChat(email, pipelineAttrs)
pipeline.run()
pipelineAttrs.getBuild().post()
} catch (e) {
jenkins.currentBuild.result = CommonStrings.FAILURE
throw e
} finally {
switch (product) {
case Product.****:
chat.notifyTeam(CommonStrings.****_WEBHOOK)
break
case Product.****:
chat.winNotifyTeam(CommonStrings.****_WEBHOOK)
break
case Product.****:
chat.notifyTeam(CommonStrings.****_WEBHOOK)
break
case Product.****:
chat.notifyTeam(CommonStrings.****_WEBHOOK)
break
case Product.****:
chat.notifyTeam(CommonStrings.****_WEBHOOK)
break
case Product.****:
chat.notifyTeam(CommonStrings.****_WEBHOOK)
break
}
chat.postbuildChat(email, pipelineAttrs)
jenkins.dir(jenkins.env.WORKSPACE) {
jenkinsUtils.cleanupWorkspace()
}
Any assistance would be greatly appreciated.
Try
String status = jenkins.currentBuild.result ?: 'SUCCESS'
Related
I would like to make a resilient slack notification class for my Jenkins instance.
Currently I just call the slackSend function from the Jenkins slack plugin. However if the call to slack fails, so does my build. This means I have a very hard dependency on Slack that I am not ok with.
I have managed to get the code to work but it is rather ugly and I would like to know if there is a better way to do this.
Firstly I have class defined in src/something/slack/slackHandler.groovy
The code looks a little like this:
package something.slack
public class slackHandler {
private String threadId = ""
private String channelName = ""
private Boolean silent = false
private Closure sender
private Logger logger
public silence(Boolean trigger) {
this.silent = trigger
}
public sendMessage(String msg) {
if (threadId == "") {
def (slackResponse, ok) = this.sendMessageTo(this.channelName, msg)
if (!ok) {
// We have tried to send a on channel. But the send failed so we can not determine the threadId.
// We should at this point store the messages and try send it again on the next call to slack.
return
}
this.setThreadId(slackResponse.threadId)
} else {
def (slackResponse, ok) = this.sendMessageTo(this.threadId, msg)
if (!ok){
// We tried to send on a threadId, it failed. We have the threadId so we can leave the slackResponse alone.
// We should at this point store the messages and try send it again on the next call to slack.
return
}
}
}
public sendMessageTo(String channel, String msg) {
return this.trySend(channel, msg)
}
private trySend(String to, String msg) {
if (this.silent) {
return [[threadId: "nothing"], true]
}
try {
return [sender(to, msg), true]
} catch (e) {
// These do not work :(
println("There wasn an error sending the slack message. Error $e")
println("Message being sent: $msg")
return [null, false]
}
}
}
This is the part I'm not happy about. To use the above code I need to create and pass a closure to send the messages to slack because the slackSend function is not available inside my class. I can not find out how to give it the ability to use this function or access the slack class. The println calls are also not showing up in the Jenkins console which is a problem for this class as the fallback is to log to the console. This makes me think that I am missing some sort of context that I need to give my class.
This is how I currently use the class:
def slackSender = new slackHandler(
[
channelName:"rd-bots",
sender: {to, msg -> return slackSend(channel: to, message: msg)}
]
)
slackSender.sendMessage("Hello :wave:")
Can someone please tell me if the is a way to pass on the context or if what I have done is the only way? Also why don't the println calls appear in the Jenkins log?
You can include a try-catch in your pipeline code around your call to your slack handler.
Here's the documentation for catchError: https://www.jenkins.io/doc/pipeline/steps/workflow-basic-steps/#catcherror-catch-error-and-set-build-result-to-failure
I think it should look something like this in a pipeline:
#Library('shared-lib#main')_
pipeline{
agent any
options {
timestamps()
}
environment {
Custom_Variables = 'stuff'
}
stages{
stage ('blah') {
steps {
catchError{
def slackSender = new slackHandler(
[
channelName:"rd-bots",
sender: {to, msg -> return slackSend(channel: to, message: msg)}
]
)
slackSender.sendMessage("Hello :wave:")
}
}
}
}
}
After a bit of digging and looking at different questions for other things where context is needed I managed to find an answer.
When running in Jenkins the this value will give the context of your running environment. You can pass that on to something else.
Code updated to look like this:
public class slackHandler {
private String threadId = ""
private String channelName = ""
private Boolean silent = false
private ctx
public silence(Boolean trigger) {
this.silent = trigger
}
public setThreadId(String id) {
this.threadId = id
}
public sendMessage(String msg) {
if (threadId == "") {
def (slackResponse, ok) = this.sendMessageTo(this.channelName, msg)
if (!ok) {
// We have tried to send a on channel. But the send failed so we can not determine the threadId.
// We should at this point store the messages and try send it again on the next call to slack.
return
}
this.setThreadId(slackResponse.threadId)
} else {
def (slackResponse, ok) = this.sendMessageTo(this.threadId, msg)
if (!ok){
// We tried to send on a threadId, it failed. We have the threadId so we can leave the slackResponse alone.
// We should at this point store the messages and try send it again on the next call to slack.
return
}
}
}
public sendMessageTo(String channel, String msg) {
return this.trySend(channel, msg)
}
private trySend(String to, String msg) {
if (this.silent) {
return [[threadId: "nothing"], true]
}
def slackResponse = ctx.slackSend(channel: to, message: msg)
if (slackResponse != null) {
return [slackResponse, true]
} else {
ctx.echo("There was an error sending slack message sent: $msg")
return [null, false]
}
}
}
Used like this:
import com.proquoai.slack.slackHandler
def slackSender = new slackHandler(
[
channelName:"trashroom10120123",
ctx: this
]
)
node ('docker') {
stage('Send Slack Messages') {
slackSender.sendMessage("Hello :wave:")
slackSender.sendMessage("It's running :go_dance:")
}
stage('Send out of band messages') {
slackSender.sendMessageTo("rd-bots", ":ship-it:")
}
}
As a side note, the slackSend function appears to swallow the error and simply doesn't return a slackResponse. Therefore using a try/catch block didn't actually help in determining if slack sending failed.
I'm using a function to set environment variables in Jenkins pipeline. I've noticed that if I declare function without argument, it works, but if I declare function that accepts 1 string argument, Jenkins throws error No such DSL method 'get_metadata' found among steps while running my pipeline.
def get_metadata(String type) {
switch(type) {
case "env":
return "environment name";
break;
case "domain":
return "domain name";
break;
case "cloud":
return "cloud name";
break;
default:
return "none";
break;
}
}
pipeline {
environment {
PROJECT=get_metadata()
CLOUD=get_metadata(type: "cloud")
DOMAIN=get_metadata(type: "domain")
ENVIRONMENT=get_metadata(type: "env")
}
}
Function without argument works when I call it like get_metadata()
def get_metadata() {
<...>
}
Jenkins version is 2.289.2.
Your get_metadata does not define a default value for type and therefore the call to PROJECT=get_metadata() throws an error, as you can't use it as is without passing the type parameter.
To solve it you can just add a default value to your function:
def get_metadata(String type = '') {
switch(type) {
case "env":
return "environment name";
case "domain":
return "domain name";
case "cloud":
return "cloud name";
default:
return "none";
}
}
I am getting the following error when trying to run my jenkins job. Any help would be much appreciated
java.lang.IllegalArgumentException: Expected named arguments but got
[org.jenkinsci.plugins.workflow.cps.CpsClosure2#33c7c4a6,
org.jenkinsci.plugins.workflow.cps.CpsClosure2#79505a8c,
org.jenkinsci.plugins.workflow.cps.CpsClosure2#6a96df3,
org.jenkinsci.plugins.workflow.cps.CpsClosure2#1a0cb771,
org.jenkinsci.plugins.workflow.cps.CpsClosure2#17e3a262] at
org.jenkinsci.plugins.workflow.cps.DSL.singleParam(DSL.java:606) at
org.jenkinsci.plugins.workflow.cps.DSL.parseArgs(DSL.java:594) at
org.jenkinsci.plugins.workflow.cps.DSL.parseArgs(DSL.java:534) at
org.jenkinsci.plugins.workflow.cps.DSL.invokeStep(DSL.java:219) at
org.jenkinsci.plugins.workflow.cps.DSL.invokeMethod(DSL.java:178) at
org.jenkinsci.plugins.workflow.cps.CpsScript.invokeMethod(CpsScript.java:122)
at sun.reflect.GeneratedMethodAccessor102.invoke(Unknown Source)
My code is:
node("dvsacvsmgmt") {
stage("Build") {
def buildJobs = []
for (BACKEND_SERVICE in BACKEND_SERVICES) {
SVC = BACKEND_SERVICE.replaceAll('-','_')
switch (BRANCH_SVC) {
case ["develop","master"]:
def buildJob = {
build "${ROOT_FOLDER}/2_Build/Backend/${SVC}/job_build_backend_" + BRANCH_SVC + "_" + SVC +".groovy"
}
buildJobs.add(buildJob)
break
default:
def buildJob = {
build "job_${SVC}": "${ROOT_FOLDER}/2_Build/Backend/${SVC}/job_build_backend_" + BRANCH_SVC + "_" + SVC +".groovy",
parameters: [gitParameter(name: "BRANCH", value: BRANCH_SVC)]
}
buildJobs.add(buildJob)
break
}
}
parallel(buildJobs)
}
}
NOTE: My variables are defined at the top,
BRANCH, BRANCH_SVC, ROOT_FOLDER, BACKEND_SERVICES
You see this exception because buildJobs in your example is a list of closures and it should be a map instead. It would make sense to use backend service name as a key for the map you pass to parallel() method. Consider the following changes to your code:
node("dvsacvsmgmt") {
stage("Build") {
def buildJobs = [:]
for (BACKEND_SERVICE in BACKEND_SERVICES) {
SVC = BACKEND_SERVICE.replaceAll('-','_')
switch (BRANCH_SVC) {
case ["develop","master"]:
def buildJob = {
build "${ROOT_FOLDER}/2_Build/Backend/${SVC}/job_build_backend_" + BRANCH_SVC + "_" + SVC +".groovy"
}
buildJobs.put(BACKEND_SERVICE, buildJob)
break
default:
def buildJob = {
build "job_${SVC}": "${ROOT_FOLDER}/2_Build/Backend/${SVC}/job_build_backend_" + BRANCH_SVC + "_" + SVC +".groovy",
parameters: [gitParameter(name: "BRANCH", value: BRANCH_SVC)]
}
buildJobs.put(BACKEND_SERVICE, buildJob)
break
}
}
parallel(buildJobs)
}
}
It invokes
buildJobs.put(BACKEND_SERVICE, buildJob)
instead
buildJobs.add(buildJob)
to create a map that is seen as named arguments in parallel method call.
None of the lines after making httpRequest are getting executed. Everything else works fine in this function. What could be going wrong here?
However, network request is going fine and I am able to see the response in the console. httpRequest is being made via plugin
I've even tried CURL - but lines after curl are not executed.
#NonCPS
def doPRCommentBasedTesting() {
def causes = currentBuild.rawBuild.getCauses()
def commentURL
for(cause in causes) {
if (cause.class.toString().contains("GitHubPullRequestCommentCause")) {
commentURL = cause.getCommentUrl()
commentURL = commentURL.substring(commentURL.lastIndexOf("-") + 1)
println "This job was caused by job " + commentURL
def url1 = "https://<git_url>/api/v3/repos/<owner>/<repo>/issues/comments/" + commentURL
def commentText = httpRequest authentication: '<auth_cred>', url: url1, consoleLogResponseBody: true
println commentText
println commentText.getClass()
println "hello world, how are you doing today?"
}
else {
println "Root cause : " + cause.toString()
}
}
println "==============================="
return 0
}
A non cps function does not have the ability to pause in between because it runs in a go. You need to put network call into a different function that is not marked as nonCPS and then it will work. In general the nonCPS block should be very small and limited to code that cannot be serialised
I have a hidden parameter in Jenkins called platformType. I want to display choices based on the parameter platformType. I created the following groovy script but it doesn't work
if (platformType.equals("android")) {
return ['7.0', '6.0']
} else (platformType.equals("ios")) {
return ['10.0', '9.0']
}
Pls see the screenshot below
quite sure you did not specify the platformType as a parameter to platformVersion or you have other error in your code..
without error handling you just don't see it.
in your script you can catch the exception like this:
try {
if (platformType.equals("android")) {
return ['7.0', '6.0']
} else if(platformType.equals("ios")) {
return ['10.0', '9.0']
}
}catch(e){ return [e.toString()] }
in this case you'll see the error in your choice field
Looks you are missing if in the else part.
It is supposed to be:
if ('android' == platformType) {
return ['7.0', '6.0']
} else if ('ios' == platformType) {
return ['10.0', '9.0']
} else return []