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