jenkinsgroovybuild

Setting Jenkins Freestyle project variables from an XML file in the checkout


I have a Jenkins freestyle project with a parameterised build to set variables in a large number of steps. For a simple example "Version" set to "1.0.0-prerelease" that could be used as %Version% in a later "Execute Windows batch command" step or $Version in some other steps.

One of the source files (specifically a MSBuild XML props file) also contains this value, so rather than updating the Jenkins project builds, I would like to read this from the XML file.

I tried to use the Environment Injector plugin with a script, however it seems this runs on the main Jenkins server not the build server and so can't access the checked out source files.

Build Environment -> Inject environment variables to the build process -> Groovy script

def text= new File("$WORKSPACE\\Example.props").text
def xml= new XmlSlurper().parseText(text)
def map = [:]
map["Version"] = xml.PropertyGroup.Version
return map
[EnvInject] - Executing scripts and injecting environment variables after the SCM step.
[EnvInject] - Evaluating the Groovy script content
[EnvInject] - [ERROR] - Problems occurs on injecting env vars defined in the build wrapper: org.jenkinsci.lib.envinject.EnvInjectException: Failed to evaluate the script. java.io.FileNotFoundException: C:\jenkins\workspace\test\Example.props (The system cannot find the path specified). See system log for more info

Is there some way to either run a script on the build node or to otherwise read a file from the source checkout?

At this time I am trying to avoid rewriting all the projects (e.g. to use pipelines, or to have a single self contained "build script" that does everything rather than many freestyle steps).


Solution

  • Your problem is indeed the fact that Groovy system script will always run on the Jenkins master node, while you are trying to access a path on the agent workspace (which of course doesn't exist on the master node).

    One solution will be to use the FilePath class instead of the File:

    public final class FilePath
    Unlike File, which always implies a file path on the current computer, FilePath represents a file path on a specific agent or the controller. Despite that, FilePath can be used much like File.
    It exposes a bunch of operations (and we should add more operations as long as they are generally useful), and when invoked against a file on a remote node, FilePath executes the necessary code remotely, thereby providing semi-transparent file operations.

    Using this class can provide you access to the remote file on your agent workspace.
    You can try something like the following:

    // Initiate the Remote Path
    def remotePath = new FilePath(build.workspace, 'Example.props')
    
    // Read the file and parse it
    def xml= new XmlSlurper().parseText(remotePath.readToString())
    def map = [:]
    map["Version"] = xml.PropertyGroup.Version
    return map