Search code examples
jenkinsjenkins-pipelinejenkins-plugins

Is it possible to use build user vars plugin in a Jenkins shared library?


I'm implementing functionality to expose the triggering user for a Jenkins pipeline to our CD system, so I've grabbed the build user vars plugin: https://plugins.jenkins.io/build-user-vars-plugin/

The way the plugin seems to work is that you wrap the code you need the variables exposed to, like so:

  wrap([$class: 'BuildUser']) {
    userId = env.BUILD_USER_ID
  }

I tried this on a general pipeline and just echoed it out, all was well.

I then tried implementing it in our shared library so that this would happen on all calls to CD, and I'm hitting an error.

wrap([$class: 'BuildUser']) {
    jobBuildUrl ="${jobBuildUrl}&USER_ID=${env.BUILD_USER_ID}"
}

[2021-08-19T10:20:22.852Z] hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: com.company.jenkins.pipelines.BuildManager.wrap() is applicable for argument types: (java.util.LinkedHashMap, org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [[$class:BuildUser], org.jenkinsci.plugins.workflow.cps.CpsClosure2@1c9a210c]

Is there any way to use this plugin code within a shared library? If so how?

I'm lead to believe not, but I thought it was worth the asking. For reference, there is this outstanding issue: https://issues.jenkins.io/browse/JENKINS-44741

Side note, I'm trying to do this without touching everybody's pipelines themselves. If this is impossible with this plugin, I'll probably just implement my own version of it within the shared lib.


Solution

  • That plugin is hard to use and has many issues, one of them is with shared libraries.
    Instead just implement it by yourself, it is relatively easy and allows you much more control over the logic of what is done, error handling and which parameters you return.
    You can use something like the following:

    /**
    * Get the last upstream build that triggered the current build
    * @return Build object (org.jenkinsci.plugins.workflow.job.WorkflowRun) representing the upstream build
    */
    @NonCPS
    def getUpstreamBuild() {
       def build = currentBuild.rawBuild
       def upstreamCause
       while (upstreamCause = build.getCause(hudson.model.Cause$UpstreamCause)) {
           build = upstreamCause.upstreamRun
       }
       return build
    }
    
    /**
    * Get the properties of the build Cause (the event that triggered the build)
    * @param upstream If true (Default) return the cause properties of the last upstream job (If the build was triggered by another job or by a chain of jobs)
    * @return Map representing the properties of the user that triggered the current build.
    *         Contains keys: USER_NAME, USER_ID
    */
    @NonCPS
    def getUserCauseProperties(Boolean upstream = true) {
       def build = upstream ? getUpstreamBuild() : currentBuild.rawBuild
       def cause = build.getCause(hudson.model.Cause$UserIdCause)
       if (cause) {
           return ['USER_NAME': cause.userName, 'USER_ID': cause.userId]
       }
       println "Job was not started by a user, it was ${build.getCauses()[0].shortDescription}"
       return [:]
    }