Skip to content

Commit

Permalink
modernise build time trend page (#9465)
Browse files Browse the repository at this point in the history
* modernise build time trend page

- use jenkins-table
- add time since column
- make the link to the console an explicit icon (like for the agent
  history)
- hide the agent column for pipeline jobs

* apply prettier

* missing let declaration

* fix tests

* change flex-direction with media

* apply prettier

* revert change to buildListTable
  • Loading branch information
mawinter69 authored Jul 22, 2024
1 parent cc97ad8 commit e9d210a
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 46 deletions.
8 changes: 8 additions & 0 deletions core/src/main/java/jenkins/widgets/BuildTimeTrend.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
package jenkins.widgets;

import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BallColor;
import hudson.model.Job;
import hudson.model.Node;
import hudson.model.Run;
import jenkins.console.ConsoleUrlProvider;
Expand All @@ -37,6 +39,10 @@
@Restricted(DoNotUse.class) // only for buildTimeTrend.jelly
public class BuildTimeTrend extends RunListProgressiveRendering {

public boolean isAbstractProject(Job<?, ?> job) {
return job instanceof AbstractProject;
}

@Override protected void calculate(Run<?, ?> build, JSONObject element) {
BallColor iconColor = build.getIconColor();
element.put("iconName", iconColor.getIconName());
Expand All @@ -46,6 +52,8 @@ public class BuildTimeTrend extends RunListProgressiveRendering {
element.put("displayName", build.getDisplayName());
element.put("duration", build.getDuration());
element.put("durationString", build.getDurationString());
element.put("timestampString", build.getTimestampString());
element.put("timestampString2", build.getTimestampString2());
element.put("consoleUrl", ConsoleUrlProvider.getRedirectUrl(build));
if (build instanceof AbstractBuild) {
AbstractBuild<?, ?> b = (AbstractBuild) build;
Expand Down
47 changes: 27 additions & 20 deletions core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ THE SOFTWARE.

<!-- Displays the chart that show how long builds are taking -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:t="/lib/hudson">
<l:layout title="${%title(it.displayName)}">
<st:include page="sidepanel.jelly" />
<l:breadcrumb title="${%Build Time Trend}" />
Expand All @@ -45,29 +45,36 @@ THE SOFTWARE.
<l:icon src="symbol-status-nobuilt-anime" id="nobuilt-anime" />
<l:icon src="symbol-status-aborted-anime" id="aborted-anime" />
<l:icon src="symbol-status-disabled-anime" id="disabled-anime" />
<l:icon src="symbol-terminal" id="console" />
</template>
<h1>${%Build Time Trend}</h1>
<div align="center">
<div id="buildTimeTrend">
<div>
<j:new var="handler" className="jenkins.widgets.BuildTimeTrend"/>
${handler.setBuilds(it.builds)}
<j:set var="showAgent" value="${!empty(app.nodes) and handler.isAbstractProject(it)}"/>
<l:progressiveRendering handler="${handler}" callback="buildTimeTrend_displayBuilds"/>
<t:setIconSize/>
<table class="jenkins-table jenkins-table--auto-width ${iconSize == '16x16' ? 'jenkins-table--small' : iconSize == '24x24' ? 'jenkins-table--medium' : ''} sortable jenkins-hidden" id="trend"
data-show-agent="${showAgent}" data-icon-size-class="${iconSizeClass}">
<thead>
<tr>
<th class="jenkins-table__cell--tight">${%S}</th>
<th initialSortDir="up">${%Build}</th>
<th>${%Time Since}</th>
<th>${%Duration}</th>
<j:if test="${showAgent}">
<th>${%Agent}</th>
</j:if>
<th class="jenkins-table__cell--tight" data-sort-disable="true"></th>
</tr>
</thead>
<tbody></tbody>
</table>
<t:iconSize/>
</div>
<img class="build-time-graph" src="buildTimeGraph/png" width="500" height="400" lazymap="buildTimeGraph/map" alt="[${%Build time graph}]" />
</div>

<j:set var="isDistributedBuildEnabled" value="${!empty(app.nodes)}"/>
<div align="center">
<j:new var="handler" className="jenkins.widgets.BuildTimeTrend"/>
${handler.setBuilds(it.builds)}
<l:progressiveRendering handler="${handler}" callback="buildTimeTrend_displayBuilds"/>
<table class="sortable" id="trend"
data-is-distributed-build-enabled="${isDistributedBuildEnabled}">
<tr>
<th><st:nbsp/></th>
<th initialSortDir="up">${%Build}</th>
<th>${%Duration}</th>
<j:if test="${isDistributedBuildEnabled}">
<th>${%Agent}</th>
</j:if>
</tr>
</table>
</div>
</l:main-panel>
</l:layout>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
img.build-time-graph {
float: right;
#buildTimeTrend {
display: flex;
gap: 15px;
}

@media (max-width: 1300px) {
#buildTimeTrend {
flex-direction: column-reverse;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,52 @@
* Public method to be called by progressiveRendering's callback
*/
window.buildTimeTrend_displayBuilds = function (data) {
var p = document.getElementById("trend");
var isDistributedBuildsEnabled =
"true" === p.getAttribute("data-is-distributed-build-enabled");
var rootURL = document.head.getAttribute("data-rooturl");
const p = document.getElementById("trend");
p.classList.remove("jenkins-hidden");

for (var x = 0; data.length > x; x++) {
var e = data[x];
var tr = document.createElement("tr");
const showAgent = "true" === p.dataset.showAgent;
const rootURL = document.head.getAttribute("data-rooturl");

for (let x = 0; data.length > x; x++) {
const e = data[x];
let tr = document.createElement("tr");

let td = document.createElement("td");
td.setAttribute("data", e.iconColorOrdinal);

let link = document.createElement("a");
link.classList.add("build-status-link");
link.href = e.consoleUrl;
td.appendChild(link);
td.classList.add("jenkins-table__cell--tight", "jenkins-table__icon");
let div = document.createElement("div");
div.classList.add("jenkins-table__cell__button-wrapper");
let svg = generateSVGIcon(e.iconName);
link.appendChild(svg);
svg.setAttribute("tooltip", e.iconColorDescription);
div.appendChild(svg);
td.appendChild(div);
tr.appendChild(td);

td = document.createElement("td");
td.setAttribute("data", e.number);

link = document.createElement("a");
let link = document.createElement("a");
link.href = e.number + "/";
link.classList.add("model-link", "inside");
link.innerText = escapeHTML(e.displayName);

td.appendChild(link);
tr.appendChild(td);

td = document.createElement("td");
td.setAttribute("data", e.timestampString2);
td.textContent = e.timestampString;
tr.appendChild(td);

td = document.createElement("td");
td.setAttribute("data", e.duration);

td.innerText = escapeHTML(e.durationString);

tr.appendChild(td);
if (isDistributedBuildsEnabled) {
var buildInfo = null;
var buildInfoStr = escapeHTML(e.builtOnStr || "");
if (showAgent) {
let buildInfo = null;
let buildInfoStr = escapeHTML(e.builtOnStr || "");
if (e.builtOn) {
buildInfo = document.createElement("a");
buildInfo.href = rootURL + "/computer/" + e.builtOn;
Expand All @@ -58,6 +64,19 @@ window.buildTimeTrend_displayBuilds = function (data) {
}
tr.appendChild(td);
}

let tdConsole = document.createElement("td");
tdConsole.classList.add("jenkins-table__cell--tight");
let div2 = document.createElement("div");
div2.classList.add("jenkins-table__cell__button-wrapper");
link = document.createElement("a");
link.classList.add("jenkins-button", "jenkins-button--tertiary");
link.href = e.consoleUrl;
link.appendChild(generateSVGIcon("console"));
div2.appendChild(link);
tdConsole.appendChild(div2);
tr.appendChild(tdConsole);

p.appendChild(tr);
Behaviour.applySubtree(tr);
}
Expand Down Expand Up @@ -132,7 +151,7 @@ window.displayBuilds = function (data) {
var div2 = document.createElement("div");
div2.classList.add("jenkins-table__cell__button-wrapper");
var a3 = document.createElement("a");
a3.classList.add("jenkins-button");
a3.classList.add("jenkins-button", "jenkins-button--tertiary");
a3.href = e.consoleUrl;
a3.innerHTML = p.dataset.consoleOutputIcon;
div2.appendChild(a3);
Expand Down
12 changes: 6 additions & 6 deletions test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void withAbstractJob_OnBuiltInNode() throws Exception {
wc.withThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.getPage(p, "buildTimeTrend");

HtmlTable table = page.getDocumentElement().querySelector("table[data-is-distributed-build-enabled=false]");
HtmlTable table = page.getDocumentElement().querySelector("table[data-show-agent=false]");
assertNotNull(table);
}

Expand All @@ -91,7 +91,7 @@ public void withAbstractJob_OnAgentNode() throws Exception {

wc.withThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.getPage(p, "buildTimeTrend");
DomNodeList<DomNode> anchors = page.getDocumentElement().querySelectorAll("table[data-is-distributed-build-enabled=true] td a");
DomNodeList<DomNode> anchors = page.getDocumentElement().querySelectorAll("table[data-show-agent=true] td a");
Optional<DomNode> anchor = anchors.stream()
.filter(a -> a.getTextContent().equals(agent.getNodeName()))
.findFirst();
Expand All @@ -115,15 +115,15 @@ public void withAbstractJob_OnBoth() throws Exception {
wc.withThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.getPage(p, "buildTimeTrend");

DomNodeList<DomNode> anchors = page.getDocumentElement().querySelectorAll("table[data-is-distributed-build-enabled=true] td a");
DomNodeList<DomNode> anchors = page.getDocumentElement().querySelectorAll("table[data-show-agent=true] td a");
Optional<DomNode> anchor = anchors.stream()
.filter(a -> a.getTextContent().equals(agent.getNodeName()))
.findFirst();
// for the build on agent
assertTrue(anchor.isPresent());

String builtInNode = hudson.model.Messages.Hudson_Computer_DisplayName();
DomNodeList<DomNode> tds = page.getDocumentElement().querySelectorAll("table[data-is-distributed-build-enabled=true] td");
DomNodeList<DomNode> tds = page.getDocumentElement().querySelectorAll("table[data-show-agent=true] td");
Optional<DomNode> td = tds.stream()
.filter(t -> t.getTextContent().equals(builtInNode))
.findFirst();
Expand All @@ -142,7 +142,7 @@ public void withNonAbstractJob_withoutAgents() throws Exception {
wc.withThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.getPage(p, "buildTimeTrend");

DomNodeList<DomNode> tds = page.getDocumentElement().querySelectorAll("table[data-is-distributed-build-enabled=false] td");
DomNodeList<DomNode> tds = page.getDocumentElement().querySelectorAll("table[data-show-agent=false] td");
Optional<DomNode> td = tds.stream()
.filter(t -> t.getTextContent().equals("#1"))
.findFirst();
Expand All @@ -168,7 +168,7 @@ public void withNonAbstractJob_withAgents() throws Exception {
wc.withThrowExceptionOnFailingStatusCode(false);
HtmlPage page = wc.getPage(p, "buildTimeTrend");

DomNodeList<DomNode> tds = page.getDocumentElement().querySelectorAll("table[data-is-distributed-build-enabled=true] td");
DomNodeList<DomNode> tds = page.getDocumentElement().querySelectorAll("table[data-show-agent=false] td");
Optional<DomNode> td = tds.stream()
.filter(t -> t.getTextContent().equals("#1"))
.findFirst();
Expand Down

0 comments on commit e9d210a

Please sign in to comment.