tcplugins / tcWebHooks

WebHooks plugin for Teamcity. Supports many build states and payload formats.
https://netwolfuk.wordpress.com/category/teamcity/tcplugins/tcwebhooks/
155 stars 30 forks source link

Calculate Queue and single Build Step Duration #215

Closed Super8film87 closed 1 year ago

Super8film87 commented 1 year ago

Question

Description

I'm currently using TeamCity webhooks with velocity templates to process build events. However, I'm facing challenges in calculating the queued duration and duration of every single build step along with their names.

Expected Solution

I would like to know if there is a recommended approach or workaround to calculate the queued duration and duration of every single build step and its name using TeamCity webhooks with velocity templates. Are there any specific variables or methods available in the velocity context that can be utilized for this purpose?

What I tried so far:

"queueTime" : ${build.QueuedDate} -  ${build.StartDate}
#set($queuedDate = $dateTool.parse("EEE MMM dd HH:mm:ss zzz yyyy", $build.QueuedDate))
#set($startDate = $dateTool.parse("EEE MMM dd HH:mm:ss zzz yyyy", $build.StartDate))
#set($durationMillis = $dateTool.diff($queuedDate, $startDate))
#set($durationSeconds = $math.div($durationMillis, 1000))
$durationSeconds

Alternatively, if this functionality is currently unavailable, I would appreciate any insights on potential enhancements or customizations that can be made to achieve this requirement.

I'm open to any suggestions, tips, or examples that can help me extract the necessary information from the webhook payload using velocity templates.

Thank you in advance for your assistance!

netwolfuk commented 1 year ago

Hi @Super8film87 You've made some good progress. You might realise that the $build object in the template is an SBuild object. There are a few versions of this interface, but for the BuildFinished events (Success, Failed, Fixed, Broken) the $build will be an SFinishedBuild.

In velocity, calling $build.startDate will call the getStartDate() method on the SFinishedBuild object.

getStateDate() returns a java.util.Date, and you can then call getTime() on it, which returns milliseconds since epoch.

So, I was able to get queue duration like this..

#set($queueDuration = (${build.StartDate.Time} - ${build.QueuedDate.Time}) /1000)
#set($buildDuration = (${build.FinishDate.Time} - ${build.StartDate.Time}) /1000)
{ 
    "queueTime" : "$queueDuration seconds",
    "buildTime" : "$buildDuration seconds",
}

which outputs...

{ 
    "queueTime" : "4 seconds",
    "buildTime" : "340 seconds",
}

I have not figured out how to get at the Build Steps.

netwolfuk commented 1 year ago

I've asked the team. https://youtrack.jetbrains.com/issue/TW-81941/How-to-access-Build-Steps-from-OpenAPI-plugin

Super8film87 commented 1 year ago

Thx for the fast feedback - Perfect that helps already a lot.

netwolfuk commented 1 year ago

Hi @Super8film87

The team at Jetbrains answered the query. Here is a working template that now includes step name and duration.

## Define macro called "getFirstFailedBuild"
#macro( getFirstFailedBuild $myBuild)
  #set ($currentBuild = $myBuild)
  #set ($previousBuild = $myBuild.getPreviousFinished())
  #set ($keepLooping = true)
  #foreach($unused in [1..50]) ## only loop 50 times in case there are hundreds of failures
    #if ($keepLooping)
      #if ($previousBuild) ## Null check 
        #if ($previousBuild.getStatusDescriptor().isSuccessful())
          #set ($keepLooping = false) ## Our $previousBuild is isSuccessful, so the $currentBuild is the last failure
          "$currentBuild.buildNumber :: $currentBuild.getStatusDescriptor().isSuccessful()" ## Output something when we find out first failure
        #end
      #else
         #set ($keepLooping = false)
         "unknown" ## text to display if getPreviousFinished returns null
      #end
      #set ($currentBuild = $previousBuild) ## set vars for next iteration
      #set ($previousBuild = $previousBuild.getPreviousFinished()) ## set vars for next iteration
    #end ## close $keepLooping
  #end
#end

#set($queueDuration = (${build.StartDate.Time} - ${build.QueuedDate.Time}) /1000)
#set($buildDuration = (${build.FinishDate.Time} - ${build.StartDate.Time}) /1000)
{ 
    "queueTime" : "$queueDuration seconds",
    "buildTime" : "$buildDuration seconds",
    "firstFailed" : #getFirstFailedBuild($build)
    "steps" :[
        #foreach($buildStep in $build.getBuildPromotion().getBuildSettings().getBuildRunners())
        #set($stepDuration = ($build.getStatisticValue("buildStageDuration:buildStep" + $buildStep.getId())) /1000)
        {
            "stepName" : "$buildStep.getName()",
            "buildStepDuration" : "$stepDuration seconds"
        },
        #end
    ]
}

For me, I get the following when run against a build with steps.


{ 
    "queueTime" : "337 seconds",
    "buildTime" : "360 seconds",
    "firstFailed" :              "908 :: true"                                                                                                      
    "steps" :[
        {
            "stepName" : "Build and Package - Maven",
            "buildStepDuration" : "244.713000 seconds"
        },
        {
            "stepName" : "SonarQube Runner",
            "buildStepDuration" : "110.644000 seconds"
        },
    ]
}

The list of values that can be used to get a statistic are on your build in the REST API. http://teamcity:8111/app/rest/builds/id:150414/statistics

The id value is the internal build number. It's in the URL when you view a specific build.

Super8film87 commented 1 year ago

Cool - will test it. Thx for the support