58

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...

Lii
  • 11,553
  • 8
  • 64
  • 88
Musaffir Lp
  • 935
  • 1
  • 9
  • 16

18 Answers18

46

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

M.S. Arun
  • 1,053
  • 13
  • 22
  • 6
    Thanks, method 3 works perfect for me! – zovorap Feb 22 '22 at 19:07
  • 1
    For Method 3, the "${}" is optional for the first line if you are interested only in userid `BUILD_TRIGGER_BY = currentBuild.getBuildCauses()[0].userId echo "BUILD_TRIGGER_BY: ${BUILD_TRIGGER_BY}"` – vinodhraj Mar 15 '23 at 18:55
  • Love this. However, it does not work for builds that were triggered from another pipeline. It is not hard to solve that though, you just need method 1 or 2 and parse `upstreamUrl`/`upstreamBuild`/api/xml to jump to the parent job and retry. – the swine Jun 15 '23 at 22:37
37

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}')
Kevin Brotcke
  • 3,765
  • 26
  • 34
  • 5
    that is actually the best answer if you can not use any plugins (like the "inject env" or "Build User Vars Plugin"). – Charles Walker Oct 10 '18 at 19:09
  • Getting information from the ${BUILD_URL}/ API /json API is very useful – Honsen Nov 14 '18 at 09:32
  • 2
    this is not working for me even if I am executing this in script section – Ajay Suwalka Feb 22 '19 at 05:15
  • @AjaySuwalka Check if Jenkins is using Dash or Bash to run your script - https://stackoverflow.com/questions/12455932/what-shell-does-jenkins-use – Jeff Welling May 14 '19 at 16:55
  • 4
    I would actually argue this is the better approach because plugins are *thee* biggest complicating factor to keeping Jenkins up to date in my experience as a Jenkins operator. – Jeff Welling May 14 '19 at 16:57
  • You can also use 'jq' to parse the JSON output. Try piping the 'curl' command above to `jq -r '.actions[1].causes[0].userId'` – tanager Aug 15 '19 at 21:15
  • This might be more generic (jq is not very intuitive!): `jq -r '.actions[] | select(.causes) | .causes[].userId'` – tanager Aug 16 '19 at 13:56
  • 2
    I needed the variables in groovy so based on this I came up with the following hack: `BUILD_CAUSE_JSON = sh ( script: "curl --silent ${BUILD_URL}/api/json | tr '{}' '\n' | grep 'Started by'", returnStdout: true ).trim() echo "BUILD_CAUSE_JSON: ${BUILD_CAUSE_JSON}" BUILD_USER_ID=new groovy.json.JsonSlurper().parseText("{${BUILD_CAUSE_JSON}}").userId echo "BUILD_USER_ID: ${BUILD_USER_ID}"` – Attila123 Aug 30 '19 at 15:32
  • any way to get the "user description"? I want to store some metadata about users there – Vitim.us Nov 25 '20 at 21:34
  • userName, userId or even text 'started by' will not be available in all cases such as when triggered due to "branch indexing" or say "started by timer". This is only present when build is triggered manually. Result is if your search was based on the above parameters, and if you try to use that value in your post scripts, your pipeline will fail. A robust solution would be to use text "shortDescription" which is always present in cause. The code is in next comment. – Pramod Yadav Dec 12 '20 at 21:47
  • // Note: Below JSON is without colon but later we use colon. `BUILD_CAUSE_JSON = sh (script: "curl --silent ${BUILD_URL}/api/json | tr '{}' '\n' | grep 'shortDescription'", returnStdout: true).trim()` // Note: There can be more than one shortDescription (during branch indexing), but only one shortDescriptionwith":" `BUILD_USER_NAME = sh (script: "echo $BUILD_CAUSE_JSON | tr ',' '\n' | grep 'shortDescription:'", returnStdout: true).trim().substring(17)` – Pramod Yadav Dec 12 '20 at 21:47
  • The Society For The Elimination of Gratuitous Use Of Grep When AWK Has A Perfectly Good Regex Filter would like to chime in and remind everyone that you can use `foo | awk '/bar/{...}` instead of `foo | grep bar | awk '{...}`. – keithpjolley Dec 26 '22 at 15:42
  • So no one got `Authentication required` after making the curl request? `curl --silent ${BUILD_URL}/api/json` or you had to provide API token or username & password – papigee Apr 28 '23 at 15:21
28

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 detailsenter image description here

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..

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
Musaffir Lp
  • 935
  • 1
  • 9
  • 16
13

Without Plugin ->

def cause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
echo "userName: ${cause.userName}"
Abhay
  • 171
  • 1
  • 4
  • 1
    Great, it worked for me with some change. This code gave me the name of the user, and in a list (or whatever it is called in groovy). What I needed though is the short username, which seems to be called user id. This worked for that (also needed to take the first element from the list): `echo "userName: ${cause.userId[0]}"` (I googled for "hudson.model.Cause UserIIdCause" and the first result https://javadoc.jenkins.io/hudson/model/Cause.UserIdCause.html helped me). Used this on Jenkins 2.263.1 – Attila123 Sep 14 '21 at 07:23
11

Install 'Build User Vars Plugin' and use like below:- [ See https://plugins.jenkins.io/build-user-vars-plugin ]

enter image description here

Be sure to check mark the Set jenkins user build variables checkbox under Build Environment for your Jenkins job's configuration.

enter image description here

Paramvir Singh Karwal
  • 597
  • 1
  • 10
  • 24
7

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.

Mike Pylypyshyn
  • 423
  • 5
  • 8
  • 3
    Just FYI to anybody who might try this - in newer versions the `getBuildCauses()` will actually be an array so you might need to do `currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')[0]['userId']` instead. +1 for the answer! :) – tftd Oct 30 '19 at 03:09
  • Also, look out for downstream jobs. We found UserIdCause to be null on downstream jobs so checked for UpstreamCause first and if found then checked build user of that build. – Sunvic Nov 25 '19 at 09:41
5

This gets the username who clicked "Build Now" in a Jenkins pipeline job.

@NonCPS
def getBuildUser() {
    return currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
}
Andy
  • 91
  • 1
  • 4
  • 4
    Two notes: 1. Required admin permission to allow that call. 2. I added a `?` right before the last dot, in order to get `null` instead of an exception. – MarkHu Aug 28 '19 at 01:39
3

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.

Dominik Gebhart
  • 2,980
  • 1
  • 16
  • 28
  • 1
    `$BUILD_CAUSE` does not include the user. – bishop Oct 21 '16 at 14:40
  • For you that is. When I do start the build manually i see my name in the message. 'BUILD_CAUSE: Gestartet durch Benutzer Dominik Gebhart' Translated: Started by user Dominik Gebhart. When its started by a Poll triggered from postCommit hook or timer, and it builds on SCM changes, it contains 'Build wurde durch eine SCM-Änderung ausgelöst.'Translated: Build triggered by SCM changes'. Do you think the content of the variable was modified by another plugin? – Dominik Gebhart Oct 21 '16 at 15:08
  • Indeed. The email-ext plugin [defines it](https://issues.jenkins-ci.org/browse/JENKINS-3166) as you say, but then the env-inject plugin [overrides it](https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin). Please note this in your answer so I can flip my vote from -1 to +1! (Vote locked until post edit.) – bishop Oct 21 '16 at 15:28
  • Have also tested if $CAUSE gets overridden too? – Dominik Gebhart Oct 22 '16 at 16:10
  • Also, it should only override when you *activate* the EvnInject in your job, not just by having this Plugin installed. – Dominik Gebhart Oct 22 '16 at 16:20
3

I'm using a combination of the 'Execute Shell' and 'Env Inject' plugin as follows:

  1. 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.
  2. Use the 'Env Inject' file to read that file as properties to set.

Jenkins build steps to default variables

bishop
  • 37,830
  • 11
  • 104
  • 139
  • 1
    This option worked for me. This is a little annoying for me as I am passing the build variables to another job and when I try to trigger a parameterzied build on another job it doesn't work when I provide the same "vars" property file. I ended up saving the file as an artifact from the original job and retrieving from the other which would be more difficult to create a template. – leeman24 Nov 30 '17 at 02:01
2

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)'
        }
           }
}
Shree Prakash
  • 2,052
  • 2
  • 22
  • 33
2

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

IPlato
  • 191
  • 2
  • 7
1

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

enter image description here

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.

ptha
  • 836
  • 1
  • 8
  • 22
1

This below is working for me.

Install "user build vars plugin"

Build Name  =   ${BUILD_NUMBER}_${TICKET}_${ENV,var="BUILD_USER_ID"}

enter image description here

Aashutosh Kumar
  • 615
  • 9
  • 13
1

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

Yakir GIladi Edry
  • 2,511
  • 2
  • 17
  • 16
1

How to return Username & UserId:

UserName: currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserName()

UserId: currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()

Yakir GIladi Edry
  • 2,511
  • 2
  • 17
  • 16
0

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 }"
Himanshu sharma
  • 7,487
  • 4
  • 42
  • 75
0

Get the current User name from the Jenkins pipeline script:

    wrap([$class: 'BuildUser']) {
    echo "BUILD_USER that started this Pipeline: ${BUILD_USER}"
    }

Output: BUILD_USER that started this Pipeline: USERNAME

Is this helpful?

Lakshmikandan
  • 4,301
  • 3
  • 28
  • 37
-1

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

KingNonso
  • 712
  • 7
  • 7