Last active
May 2, 2018 18:30
-
-
Save ChrisMBarr/d7ee9666f5d75f3cbecd63a6c347efe5 to your computer and use it in GitHub Desktop.
Show a visual representation of how long each TFS build task took
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.buildvnext-build-details-header .summary .reason, | |
.buildvnext-build-details-header .summary .duration{ | |
width: 60%; | |
} | |
.buildvnext-build-details-header .summary { | |
padding-right: 106px; | |
} | |
#timeline-bar-chart { | |
height: 42px; | |
border: 1px solid #c7e0f4; | |
display: flex; | |
float: right; | |
width: 40%; | |
} | |
#timeline-bar-chart .bar { | |
background: #71afe5; | |
flex-grow: 1; | |
min-width: 5px; | |
} | |
#timeline-bar-chart .bar:nth-child(even){ | |
background: #c7e0f4; | |
} | |
#timeline-bar-chart .bar:hover { | |
opacity: .8; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
function getDurationInMills(step) { | |
const start = new Date(step.startTime); | |
const finish = new Date(step.finishTime); | |
return finish.getTime() - start.getTime(); | |
} | |
function millsToFriendlyTime(mills) { | |
const totalSeconds = (mills / 1000); | |
const divisor_for_minutes = totalSeconds % (60 * 60) | |
const minutes = Math.floor(divisor_for_minutes / 60); | |
let seconds = Math.floor(divisor_for_minutes % 60); | |
if (seconds < 10) { | |
seconds = '0' + seconds; | |
} | |
return `${minutes}:${seconds}`; | |
} | |
function getParsedSteps(buildSteps) { | |
const orderedBuildSteps = buildSteps.sort((a, b) => { | |
if(a.order < b.order) return -1; | |
if(a.order > b.order) return 1; | |
return 0; | |
}); | |
let totalDuration = 0; | |
let parsedSteps = []; | |
for (const step of orderedBuildSteps) { | |
if(step.type === 'Task') { | |
const duration = getDurationInMills(step); | |
totalDuration += duration; | |
parsedSteps.push({ | |
name: step.name, | |
duration: duration, | |
friendlyDuration: millsToFriendlyTime(duration), | |
percent: 0 | |
}); | |
} | |
} | |
//Now we can calculate the percentages for each step | |
parsedSteps = parsedSteps.map(s => { | |
s.percent = parseFloat(((s.duration / totalDuration) * 100).toFixed(2)); | |
return s; | |
}) | |
return parsedSteps; | |
} | |
function getChartHtml(steps) { | |
let html = '<div id="timeline-bar-chart">' | |
for (const item of steps) { | |
html += `<div class="bar" style="width: ${item.percent}%;" title="${item.name} - ${item.friendlyDuration} (${item.percent}%)"></div>`; | |
} | |
html += '</div>' | |
return html; | |
} | |
function init($el) { | |
const baseUrl = __vssPageContext.webContext.collection.uri; | |
const projectId = __vssPageContext.webContext.project.id; | |
const buildIdPattern = location.search.match(/buildId=(\d+)/); | |
if (buildIdPattern && buildIdPattern[1]) { | |
const buildId = buildIdPattern[1]; | |
$.getJSON(`${baseUrl}/${projectId}/_apis/build/builds/${buildId}`) | |
.then(build => { | |
//We can't show a graph if the build is not done yet. | |
if (build.status === 'completed') { | |
$.getJSON(`${baseUrl}/${projectId}/_apis/build/builds/${buildId}/Timeline`) | |
.then(d => { | |
const steps = getParsedSteps(d.records); | |
const chartHtml = getChartHtml(steps); | |
$el.prepend(chartHtml); | |
}); | |
} | |
}); | |
} | |
} | |
//This runs quickly, so we need to make sure we have jQuery and the related elements available before we can do anything | |
let initAttempts = 0 ; | |
let elementCheckerTimer = setInterval(() => { | |
if(typeof $ === 'function') { | |
const $el = $('#buildvnext-view-result .summary') | |
if($el.length > 0 && $el.text() !== '') { | |
init($el); | |
clearInterval(elementCheckerTimer); | |
} | |
} | |
//Make sure it doesn't run forever if it fails after a while | |
initAttempts++; | |
if(initAttempts > 30) { | |
clearInterval(elementCheckerTimer); | |
} | |
}, 250); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
^https://tfs.example-company.com/tfs/ExampleCollection/.+/_build/index |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What Is This?
Use this if you use TFS, when you view a build this will get the duration of each build task and show them visually as a stacked bar chart so you can get an idea of how long each task is taking.
How to Install
Step 1: Use Chrome & Install the Personalized Web Options extention.
Step 2 Go into the extension options and create a new rule.
Step 3 Add the text above into the corresponding "Match URL" and "Add HTML" fields.
(NOTE: The script that does the magic here in enclosed in
<script>
tags and must be placed in the HTML field, and not into the script field of the extension! This allows it to have access to the HTML nodes that it needs.)