in src/BuildHistoryDisplay.js [463:618]
body: summarize_job(key),
});
}
});
}
consecutive_failure_count.forEach((v, key) => {
// Don't produce notifications for initial failure!
if (
this.state.consecutive_failure_count &&
!this.state.consecutive_failure_count.has(key)
) {
// It's failed!
new Notification("❌ " + this.props.job, {
body: summarize_job(key),
});
}
});
}
// TODO: This can cause spurious state updates
this.setState(data);
}
render() {
function result_icon(result) {
if (is_success(result))
return (
<span role="img" style={{ color: "green" }} aria-label="passed">
0
</span>
);
if (is_failure(result))
return (
<span role="img" style={{ color: "red" }} aria-label="failed">
X
</span>
);
if (is_aborted(result))
return (
<span role="img" style={{ color: "gray" }} aria-label="cancelled">
.
</span>
);
if (is_pending(result))
return (
<span
className="animate-flicker"
role="img"
style={{ color: "goldenrod" }}
aria-label="in progress"
>
?
</span>
);
return result;
}
// Sigh... the place where you can get the information you're
// interested in at the top level is NOT the same as where you get
// it inside, because of how Jenkins handles depth (Jenkins
// *will* give you information for everything recursively, just
// not in the place you might expect it.
//
// class: "com.tikal.jenkins.plugins.multijob.MultiJobBuild"
// id: "3772"
// subBuilds:
// 0:
// jobName: "whatever"
// build:
// class: "com.tikal.jenkins.plugins.multijob.MultiJobBuild"
// subBuilds:
let builds = this.state.builds;
let consecutive_failure_count = this.state.consecutive_failure_count;
const known_jobs = this.state.known_jobs;
const known_jobs_head = known_jobs.map((jobName) => (
<th className="rotate" key={jobName}>
<div
className={
(jobs_on_pr.some((e) => summarize_job(jobName).startsWith(e))
? "pr-header"
: "master-only-header") +
" " +
(consecutive_failure_count.has(jobName) ? "failing-header" : "")
}
>
{jobs_on_pr.some((e) => summarize_job(jobName).startsWith(e))
? ""
: "• "}
{summarize_job(jobName)}
</div>
</th>
));
// const known_jobs_head = known_jobs.map((jobName) =>
// <th key={jobName}></th>
//);
const durationWidth = 100;
const durationHeight = 10;
const durationScale = d3.scaleLinear().rangeRound([0, durationWidth]);
durationScale.domain([0, d3.max(builds, (b) => b.duration)]);
const seen_prs = new Set();
const rows = builds.map((build) => {
const sb_map = build.sb_map;
function perf_report(sb, result) {
return (
<Fragment>
<span
className={
is_success(result) ? "ok-duration" : "suspect-duration"
}
>
{parse_duration(sb.duration) / 1000}
</span>
</Fragment>
);
}
// let cumulativeMs = 0;
let cost = 0;
let unknownCost = false;
let inProgressCost = false;
let found = false;
const status_cols = known_jobs.map((jobName) => {
const sb = sb_map.get(jobName);
let cell = <Fragment />;
if (sb !== undefined) {
found = true;
const dur = parse_duration(sb.duration);
// cumulativeMs += dur;
const node = classify_job_to_node(getJobName(sb));
let this_cost = 0;
if (node === "unknown") {
unknownCost = true;
} else {
this_cost = Math.ceil((centsPerHour[node] * dur) / 1000 / 60 / 60);
}
cost += this_cost;
if (!sb.result) inProgressCost = true;
if (this.props.mode === "perf") {
cell = perf_report(sb, sb.result);
} else if (this.props.mode === "cost") {
cell = (
<Fragment>
{node === "unknown" ? "?" : this_cost}
</Fragment>
);
} else {
var cellHref = sb.url;
if (/^https?:\/\//.test(cellHref)) {