15
votes

Is there a way to get a list of RUNNING builds in Jenkins via a System Groovy Script? I tried looping through the busy executors, but from an executor object, I cannot get the build object:

def busyExecutors = Jenkins.instance.computers
                                .collect { 
                                  c -> c.executors.findAll { it.isBusy() }
                                }
                                .flatten() // reminder: transforms list(list(executor)) into list(executor)

busyExecutors.each { e -> 
  println('=====print out methods of executor object=======');
  println e.metaClass.methods*.name.sort().unique();

}

I can also target the JOB that I'm interested in like so:

def item = hudson.model.Hudson.instance.getItem("my_job");
println item.metaClass.methods*.name.sort().unique(); 

But then I will have to loop through 100s (if not more) builds and ask each build if they are running.

There has to be an easier/better way of getting a list of running builds.

There is a lot of information on how to do various things via System Groovy Scripts (some of which I wrote), but I cannot figure out how to get a list of running builds:

How to get currently running job's node name in jenkins using groovy

https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+Script+Console

https://gist.github.com/dnozay/e7afcf7a7dd8f73a4e05

How to make a Jenkins/Hudson job surveil some others jobs and decide whether to build or not?

3
Realized I have the same problem. Also a gotcha is that looping through executors only includes the slaves, not the master. PS, see my example below. To get the builds for a job you can use _getRuns()Steven the Easily Amused
Hint import jenkins.model.Jenkins at top of script.gaoithe
I wrote an an answer to this over on Devops.se where I used Groovy script to walk the API to find the info. It's a bit ugly, but Jenkins' API doesn't tend to provide a lot of nice helper methods, so that's not too surprising.Craig Ringer

3 Answers

15
votes

I found a couple ways to do this without using the REST API or parsing XML:

runningBuilds = Jenkins.instance.getView('All').getBuilds().findAll() { it.getResult().equals(null) }

This assumes that you have not deleted or modified the default "All" view in Jenkins. Of course you can substitute a different view name if you know exactly which view your builds are going to be in. Or you can try this approach:

runningBuilds = Jenkins.instance.getItems().collect { job->
  job.builds.findAll { it.getResult().equals(null) }
}.flatten()

Although this approach doesn't require a view name, it also has limitations. It won't descend into folders or Multibranch Pipelines or anything like that. You'll need to manually descend into folders or concoct some way of doing it automatically. For instance, here's a version that works for a Multibranch Pipeline:

Jenkins.instance.getItemByFullName(multibranchPipelineProjectName).getItems().each { repository->
  repository.getItems().each { branch->
    branch.builds.each { build->
      if (build.getResult().equals(null)) {
        // do stuff here ...
      }
    }
  }
}

I think there may be a more accurate method to use than build.getResult().equals(null) to determine if a build is running or not, but I'm having trouble finding good API docs, so I'm not sure. This was just the first method that I found using object introspection that worked.

Again due to the lack of API docs, I'm not sure if there's a significant difference between Jenkins.instance.getItems() which I used here and Jenkins.instance.getAllItems() which was used in this answer.

Finally, note that these are all relatively inefficient methods. They iterate over every build of every job, so if you save a long history of builds (the default setting is to save a history of only 10 builds per job) or have thousands of jobs, this may take a while to run. See How do I Efficiently list **All** currently running jobs on Jenkins using Groovy for a question that asks how to do this task more efficiently.

3
votes

This is not particularly efficient (but MUCH more efficient than using the API). It will print out all currently running builds with an HTML link. Can be run in script console or via scriptler.

def now = new Date()  // Get the current time
// Get a list of all running jobs
def buildingJobs = Jenkins.instance.getAllItems(Job.class).findAll { 
  it.isBuilding() }

buildingJobs.each { job->
    // Enumerate all runs
    allRuns = job._getRuns()
    allRuns.each { item ->
        // If NOT currently building... check the next build for this job
        if (!item.isBuilding()) return

        // Access and calculate time information for this build.
        def startedAt = new Date(item.getStartTimeInMillis())
        def duration_mins = ((now.getTime() - item.getStartTimeInMillis()) / 60000).intValue()

        estDurationMins = (item.getEstimatedDuration() / 60000).intValue()
        String jobname = item.getUrl()
        jobname = jobname.replaceAll('job/', '')  // Strip redundant folder info.

        println "${duration_mins.toString().padLeft(5)}/" +
            "${estDurationMins.toString().padLeft(4)}  - " +
            "<a href=${baseURL}${item.getUrl()}>${jobname}</a>"
    }
}
2
votes

You can use the REST API to get the list of running builds. Using the follow url:

http://myjenkins/jenkins/computer/api/xml?depth=1

You will get the follow response which contains <executor> elements. Only running builds has a <url> element inside <executor>. Note also that running builds has <idle>false</idle> value:

<computerSet>
    <busyExecutors>1</busyExecutors>
    <computer>
        ...
        <executor>
            <idle>true</idle>
            <likelyStuck>false</likelyStuck>
            <number>0</number>
            <progress>-1</progress>
        </executor>
        <executor>
            <currentExecutable>
                <number>328</number>
                <!-- This is the url from the current running build -->
                <url>http://myJenkins/jenkins/job/someJob/328/</url>             
            </currentExecutable>
            <currentWorkUnit/>
            <idle>false</idle>
            <likelyStuck>false</likelyStuck>
            <number>1</number>
            <progress>24</progress>
        </executor>
        ...
    </computer> 
<computerSet>

Hence use the REST API with an XPath for url to get only the running builds (note that &wrapper parameter is the name of the root xml element to avoid errors when the XPath not match or returns more than one node):

http://myJenkins/jenkins/computer/api/xml?depth=1&xpath=//url&wrapper=builds

You'll get something like:

<builds>
    <url>
http://myJenkins/jenkins/job/someJob/300/
    </url>
    <url>
http://myJenkins/jenkins/job/another/332/
    </url>
</builds>

So in Groovy you can GET the REST API, parse the returned Xml and then apply a regex to each <url> to get the data from running builds:

// get the xml from the rest api
def builds = 'http://myJenkins/jenkins/computer/api/xml?depth=1&xpath=//url&wrapper=builds'.toURL().text
// parse the xml result
def xml = new XmlSlurper().parseText(builds)
// for each url get the job name
def jobNames = xml.url.collect{ url ->
    // regex to get the jobName and a build number
    def group = (url =~ /.*\/job\/([^\/]+)\/(\d+)/)
    println group[0] // [http://myJenkins/jenkins/job/someJob/300, someJob, 300]
    def jobName = group[0][1]
    return jobName
    // to get the build number
    // def buildNr = group[0][2]
}
println jobNames