Search code examples
jenkinsgroovymultibranch-pipeline

Jenkins Declarative Pipeline - Set PATH from shared library


I have a bunch of multibranch declarative pipelines that use the same shared library. In every pipeline I need to do define a custom path:

environment {
    PATH = "$PATH:$HOME/my/cool/scripts:$HOME/some/other/scripts:/yet/another/path"
}

I thought I could move this into a shared library and avoid having to duplicate it in every Jenkinsfile:

// shared library
class Utilities {
    public static String PATH = "$PATH:$HOME/my/cool/scripts:$HOME/some/other/scripts:/yet/another/path"
}

// in Jenkinsfile
environment {
    PATH = "${Utilities.PATH}"
}

When I tried this, it complains in the shared library that $PATH and $HOME aren't set. So I realized Groovy is trying to do variable substitution due to the double quotes and changed it to this:

class Utilities {
    public static String PATH = '$PATH:$HOME/my/cool/scripts:$HOME/some/other/scripts:/yet/another/path'
}

But then variable substitution doesn't happen when it's used in the Jenkinsfile, so the actual PATH on the shell ends up being something the literal string $PATH:$HOME/my/cool/scripts:$HOME/some/other/scripts:/yet/another/path which obviously doesn't work.

I've been googling for days and I don't know where to go from here. I'm struggling to understand variable scoping in a Jenkinsfile vs the shell. Going back to this example:

environment {
    PATH = "$PATH:$HOME/my/cool/scripts:$HOME/some/other/scripts:/yet/another/path"
}

I would think the literal value of PATH is being passed to the shell and the variable substitution is happening in bash. That doesn't seem to be the case however. That would have to mean that variables from the shell such as $HOME are available in the Jenkinsfile?

Is there a way to define the PATH in a shared library so I don't have to duplicate it in every pipeline?


Solution

  • Jenkins injects the new value directly without resolving env vars in it, so the only option is to use current values and string interpolations.

    Make sure you pass PATH from the node, not the master.

    Something like this.

    static String newPath(env) {
        return "${env.PATH}:my-path"
    }
    
    pipeline {
       agent none
       stages {
           stage ('Preparation') {
              agent { label 'my-node'}
              environment {       
                  PATH = newPath(env)
              }
              steps {
                  echo PATH
              }
          }
       }
    }
    

    The example below doesn't use shell env vars but uses string interpolation instead, groovy sees a var in the string and tries to resolve it, it is configured to delegate it to the main script and jenkins adds env vars as the main script's properties.

    environment {
        PATH = "$PATH:$HOME/my/cool/scripts:$HOME/some/other/scripts:/yet/another/path"
    }