I builded Jenkins with parameters in my configuration:
a = a$
b = b$$
c = c$$$
d = d$$$$
e = e$e$e$e$
I builded my pipeline and it contained this possibilities how to display content of environment variables:
sh """
echo "${env.a}"
echo "\$a"
echo "${env.b}"
echo "\$b"
echo "${env.c}"
echo "\$c"
echo "${env.d}"
echo "\$d"
echo "${env.e}"
echo "\$e"
"""
It returned:
+ echo 'a$'
a$
+ echo 'a$$'
a$$
+ echo 'b$'
b$
+ echo 'b$$'
b$$
+ echo c14
c14
+ echo 'c$$$$'
c$$$$
+ echo d14
d14
+ echo 'd$$$$'
d$$$$
+ echo 'eeee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$14'
eeee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$14
+ echo 'ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$'
ee$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$e$$$$
Anyone who can explain this behaviour?
First of all - very interesting problem. Results are... unusual :)
I couldn't reproduce 100% of your results on my Jenkins, it's probably version/platform dependent. But fortunately the most interesting parameter e returned all that mess. So I'll focus on in.
It looks like parameters are evaluated before they are passed to environment. So each occurrence of $e is replaced by current value of e variable (which is e$e$e$e$ so far). So, we have e then three times $e replaced by e$e$e$e$ and finally $. The result is: e e$e$e$e$ e$e$e$e$ e$e$e$e$ $ (spaces added for readability). And this is value stored in environment. You can see it printed by your last echo (but with every $ replaced by $$).
The longest output (echo "${env.e}") doesn't do anything new - it just takes ee$e$e$e$e$e$e$e$e$e$e$e$$ and evaluates it one more time, replacing every occurrence of $e by ee$e$e$e$e$e$e$e$e$e$e$e$$. And finally, replaces every $ by $$ when printing :)
I want to run an external shell command (for example, git clone) inside a Jenkins pipeline.
I have found 2 ways of doing this.
This one works:
steps {
sh "git clone --branch $BRANCH --depth 1 --no-single-branch $REMOTE $LOCAL
}
Downsides:
I only see the output when the complete command is finished. Which is annoying if the command takes a long time.
I need to do some Groovy scripting to look up values in a Map, based on parameters chosen by the user who starts the build. Haven't found a way to do that without a script {} block.
A variation is to run a Bash script that runs the git clone command, that also works. Which will get me into trouble when running on Windows nodes.
The next one errors on
fatal: could not create work tree dir 'localFolder'.: Permission denied
steps {
script {
def localFolder = new File(products[params.PRODUCT].local)
if (!localFolder.exists()) {
def gitCommand = 'git clone --branch ' + params.BRANCH + ' --depth 1 --no-single-branch ' + products[params.PRODUCT].remote + ' ' + localFolder
runCommand(gitCommand)
}
}
}
This is the runCommand() wrapper:
def runCommand = { strList ->
assert ( strList instanceof String ||
( strList instanceof List && strList.each{ it instanceof String } ) \
)
def proc = strList.execute()
proc.in.eachLine { line -> println line }
proc.out.close()
proc.waitFor()
print "[INFO] ( "
if(strList instanceof List) {
strList.each { print "${it} " }
} else {
print strList
}
println " )"
if (proc.exitValue()) {
println "gave the following error: "
println "[ERROR] ${proc.getErrorStream()}"
}
assert !proc.exitValue()
}
My question is: how come I have permission to create directories when running a sh command, and how come I don't have that permission when I do the same thing inside a script {} block with .execute()?
I'm intentionally using the example of the git clone command to avoid getting answers that don't read the question, like using a dir {} block. If I can't create the git directory, then I can also not create the files inside that directory.
If you want to run any shell commands, use sh step, not Groovy's process execution. There is one main reason for that - any Groovy code you execute inside the script block, gets executed on the master node. And this is (probably) the reason you see this permission denied issue. The sh step executes on the expected node and thus creates a workspace there. And when you execute a Groovy code that is designed to create a folder in your workspace, it fails, because there is no workspace on a master node.
"1. Except for the steps themselves, all of the Pipeline logic, the Groovy conditionals, loops, etc execute on the master. Whether simple or complex! Even inside a node block!"
Source: https://jenkins.io/blog/2017/02/01/pipeline-scalability-best-practice/#fundamentals
However, there is a solution to that. You can easily combine the sh step with the script block. There is no issue with using any of the existing Jenkins pipeline steps inside the script block. Consider the following example:
steps {
script {
def localFolder = products[params.PRODUCT].local
if (!fileExists(file: localFolder)) {
sh 'git clone --branch ' + params.BRANCH + ' --depth 1 --no-single-branch ' + products[params.PRODUCT].remote + ' ' + localFolder
}
}
}
Keep in mind that this example uses fileExists and readFile steps to check if file exists in the workspace, as well as to read its content. Using new File(...) won't work correctly when your workspace is shared between master and slave nodes.
If you want to safely create files in the workspace(s), use writeFile step to make sure that the file is created on the node that executes your pipeline's current stage.
A solution to my problem:
Don't bother with showing output as a command progresses, just deal with it that I will only see it at the end.
Compose the entire command inside a script {} block.
put a sh statement inside the script {} block.
Like this:
steps {
script {
def localFolder = new File(products[params.PRODUCT].local)
if (!localFolder.exists()) {
def gitCommand = 'git clone --branch ' + params.BRANCH + ' --depth 1 --no-single-branch ' + products[params.PRODUCT].remote + ' ' + localFolder
sh gitCommand
}
}
}
This still doesn't answer my question about the permission issue, I would still like to know the root cause.
My scenario is, I have parameterized build and inside the build section, I have executed shell where I define a variable and then echo to print it. But it doesn't print anything in the console output.
I hope I have made myself clear. Could anyone please answer my question?
current_folder=`date +%Y%m%d-%H%M%S`
echo $current_folder
enter image description here
I'm using Jenkins ver. 2.32.3 and a simple freestyle job, running on mac OS, using an execute shell build step of:
current_folder=`date +%Y%m%d-%H%M%S`
echo $current_folder
Gives output of:
$ /bin/sh -xe /var/folders/kh/4fl0eeldofefmmsfd/T/hudson89388543547899686.sh
++ date +%Y%m%d-%H%M%S
+ current_folder=20180613-081712
+ echo 20180613-081712
20180613-081712
Finished: SUCCESS
In a similar fashion, setting the shell:
#!/bin/bash
current_folder=`date +%Y%m%d-%H%M%S`
echo $current_folder
Gives:
$ /bin/bash /var/folders/kh/by0kd93dfew5fgjhy000h6/T/hudson62702345565786787.sh
20180613-081655
Finished: SUCCESS
The same applies to a parameter that is defined as part of the Jenkins job, underneath the
This project is parameterized checkbox once set. For example, if you have a string parameter called userName with a default value of User1, then you can print it's value in an Execute Shell build step using:
echo $userName
echo ${userName}
echo "In a string ${userName}"
Giving:
User1
User1
In a string User1
Possible to send an email if a Jenkins 'Build Passes' after a Build Failure?
For example I have x3 builds that fail
But the fourth build passes
Is it then possible to send email to say that the build has passed
You can achieve it by using shell script in Jenkins.
Get current build status
CURRENT_BUILD_STATUS=$(curl --silent ${BUILD_URL}api/json | jq -r '.result')
Get previous build number
prevBuildNo=$(($BUILD_NUMBER-1))
echo $prevBuildNo
Get previous build status
PREVIOUS_BUILD_STATUS=$(curl -- silent http://jenkins.org.com/jenkins/job/job/jobname/${prevBuildNo}/api/json | jq -r '.result)
Cheking if the previous build was failed and the current build is successful
if ["$CURRENT_BUILD_STATUS" = "SUCCESS"] && ["$PREVIOUS_BUILD_STATUS" = "FAILED"]
then
#sending the mail
echo $ echo "hello world" | mail -s "a subject" someone#somewhere.com
else
echo "No e-mail trigger as the previous build was successful."
fi
I wanted to show the user who triggered a Jenkins job in the post job email. This is possible by using the plugin Build User Vars Plugin and the env variable BUILD_USER.
But this variable do not get initialized when the job is triggered by a scheduler.
How can we achieve this? I know we have a plugin called - EnvInject Plugin, and that can be used...
But I just want to know how we can use this and achieve the solution...
Build user vars plugin wasn't working for me so I did a quick-and-dirty hack:
BUILD_CAUSE_JSON=$(curl --silent ${BUILD_URL}/api/json | tr "{}" "\n" | grep "Started by")
BUILD_USER_ID=$(echo $BUILD_CAUSE_JSON | tr "," "\n" | grep "userId" | awk -F\" '{print $4}')
BUILD_USER_NAME=$(echo $BUILD_CAUSE_JSON | tr "," "\n" | grep "userName" | awk -F\" '{print $4}')
SIMPLE SOLUTIONS (NO PLUGINS) !!
METHOD 1: Via Shell
BUILD_TRIGGER_BY=$(curl -k --silent ${BUILD_URL}/api/xml | tr '<' '\n' | egrep '^userId>|^userName>' | sed 's/.*>//g' | sed -e '1s/$/ \//g' | tr '\n' ' ')
echo "BUILD_TRIGGER_BY: ${BUILD_TRIGGER_BY}"
METHOD 2: Via Groovy
node('master') {
BUILD_TRIGGER_BY = sh ( script: "BUILD_BY=\$(curl -k --silent ${BUILD_URL}/api/xml | tr '<' '\n' | egrep '^userId>|^userName>' | sed 's/.*>//g' | sed -e '1s/\$/ \\/ /g'); if [[ -z \${BUILD_BY} ]]; then BUILD_BY=\$(curl -k --silent ${BUILD_URL}/api/xml | tr '<' '\n' | grep '^shortDescription>' | sed 's/.*user //g;s/.*by //g'); fi; echo \${BUILD_BY}", returnStdout: true ).trim()
echo "BUILD_TRIGGER_BY: ${BUILD_TRIGGER_BY}"
}
METHOD 3: Via Groovy
BUILD_TRIGGER_BY = "${currentBuild.getBuildCauses()[0].shortDescription} / ${currentBuild.getBuildCauses()[0].userId}"
echo "BUILD_TRIGGER_BY: ${BUILD_TRIGGER_BY}"
OUTPUT:
Started by user Admin / user#example.com
Note: Output will be both User ID and User Name
This can be done using the Jenkins Build User Vars Plugin which exposes a set of environment variables, including the user who started the build.
It gives environment variables like BUILD_USER_ID, EMAIL, etc.
When the build is triggered manually by a logged-in user, that user's userid is available in the BUILD_USER_ID environment variable.
However, this environment variable won't be replaced / initialized when the build is automatically triggered by a Jenkins timer / scheduler.
Attached a screenshot for details
This can be resolved by injecting a condition to the Job by using Conditional Build Step Plugin / Run Condition Plugin,where in to each job we can add a condition to initialize the variable BUILD_USER_ID only when the build is caused or triggered by the Timer or scheduler, by setting a condition using the regular expression..
Without Plugin ->
def cause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
echo "userName: ${cause.userName}"
Install 'Build User Vars Plugin' and use like below:- [ See https://plugins.jenkins.io/build-user-vars-plugin ]
Be sure to check mark the Set jenkins user build variables checkbox under Build Environment for your Jenkins job's configuration.
I found similar but really working on Jenkins 2.1.x and easy for my understanding way.
And it works without any plugins.
if (currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')['userId']){
// Will be run only if someone user triggers build
// Because in other cases this contructions returns null
}
You can use in this construction any classes described here.
They will be returns maps with usable values.
This gets the username who clicked "Build Now" in a Jenkins pipeline job.
#NonCPS
def getBuildUser() {
return currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
}
I'm using a combination of the 'Execute Shell' and 'Env Inject' plugin as follows:
Create an 'Execute Shell' build step that uses shell parameter substitution to write default the value and echo that value into a file. Example highlighted in screen shot below.
Use the 'Env Inject' file to read that file as properties to set.
The token $BUILD_CAUSE from the email-ext plugin is what you are looking for.
You can see the full content token reference when you click the ? just after the Attach build log combobox at the email content configuration.
Some tokens get added by plugins, but this one should be aviable by default.
Edit: As pointed out by bishop in the comments, when using the EnvInject plugin, the $BUILD_CAUSE token gets changed to behave differently.
I have written a groovy script to extract the started by which would correctly get the source, regardless if user, scm or timer (could add more). It would recursively navigate the build tree to get the "original" 'started by' cause https://github.com/Me-ion/jenkins_build_trigger_cause_extractor
I wanted to trigger build initiator info to one of my slack/flock group so I used following way to get build initiator email and name by writing in Declarative fashion .
I am just printing here, you can use to store in some environment variable or write in one file giving file path according to your own convenience..
pipeline {
environment {
BRANCH_NAME = "${env.BRANCH_NAME}"
}
agent any
stages{
stage('Build-Initiator-Info'){
sh 'echo $(git show -s --pretty=%ae)'
sh 'echo $(git show -s --pretty=%an)'
}
}
}
Just to elaborate on Musaffir Lp's answer. The Conditional Build Step plugin now supports the Build Cause directly - it requires the Run Condition Plugin also.
If you wanted to detect when the build was started by a timer you can select a Run? value of Build Cause, with Build Cause of: TimerTrigger
This is a little simpler and more robust than using a regex. There are also other triggers you can detect, for example when the build was a result of Source Control Management commit, you can select: SCMTrigger.
This below is working for me.
Install "user build vars plugin"
Build Name = ${BUILD_NUMBER}_${TICKET}_${ENV,var="BUILD_USER_ID"}
I created a function that return the Triggered Job Name:
String getTriggeredJob(CURRENT_BUILD) {
if (CURRENT_BUILD.upstreamBuilds.size() > 0) {
TRIGGERED_JOB = CURRENT_BUILD.upstreamBuilds[0].projectName
if (!TRIGGERED_JOB.isEmpty()) {
return TRIGGERED_JOB
}
}
return "Self"
}
CURRENT_BUILD is env var currentBuild
How to return Username & UserId:
UserName: currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserName()
UserId: currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
There is other way to get user_id, where you don't need to install anything.
BUILD_USER_ID = sh (
script: 'id -u',
returnStdout: true
).trim()
echo "bUILD USER: ${BUILD_USER_ID }"
For declarative pipeline syntax, here is a quick hack, base on #Kevin answer.
For declarative pipeline you need to enclose them in a node, else you will get an error/ build failure
node {
def BUILD_FULL = sh (
script: 'curl --silent '+buildURL+' | tr "{}" "\\n" | grep -Po \'"shortDescription":.*?[^\\\\]"\' | cut -d ":" -f2',
returnStdout: true
)
slackSend channel: '#ci-cd',
color: '#000000',
message: "The pipeline was ${BUILD_FULL} ${GIT_COMMIT_MSG} "
}
The output will be slack notification sent to your slack channel with the git short description