Skip to content

Commit d043aeb

Browse files
committed
Deploy a devhub site
1 parent 461b573 commit d043aeb

File tree

5 files changed

+494
-0
lines changed

5 files changed

+494
-0
lines changed

bin/build-rust-docs.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ cp -a docs/dev/book/. build/docs/dev
9090
mkdir -p build/docs/shared
9191
cp -a docs/shared/. build/docs/shared
9292

93+
mkdir -p build/docs/devhub
94+
cp -a tools/devhub/www/. build/docs/devhub
95+
9396
mkdir -p build/docs/docs
9497
cp -a target/doc/. build/docs/docs
9598
printf '<meta http-equiv=refresh content=0;url=%s/index.html>\n' "$CRATE_NAME" > build/docs/docs/index.html

tools/devhub/www/apexcharts.min.js

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/devhub/www/devhub.js

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// Code powering "developer dashboard" aka devhub, at <https://mozilla.github.io/glean/devhub>.
2+
// Based on <https://github.com/tigerbeetle/tigerbeetle/blob/b6d541562290f23c10760ea20b559cf21b9010b0/src/devhub/devhub.js>
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
window.onload = () =>
7+
Promise.all([
8+
main_metrics(),
9+
]);
10+
11+
function assert(condition) {
12+
if (!condition) {
13+
alert("Assertion failed");
14+
throw "Assertion failed";
15+
}
16+
}
17+
18+
async function main_metrics() {
19+
const data_url = "https://raw.githubusercontent.com/mozilla/glean-devhubdb/main/devhub/data.json";;
20+
const data = await (await fetch(data_url)).text();
21+
const max_batches = 200;
22+
const batches = data.split("\n")
23+
.filter((it) => it.length > 0)
24+
.map((it) => JSON.parse(it))
25+
.slice(-1 * max_batches)
26+
.reverse();
27+
28+
const series = batches_to_series(batches);
29+
plot_series(series, document.querySelector("#charts"), batches.length);
30+
}
31+
32+
function format_duration(duration_ms) {
33+
const milliseconds = duration_ms % 1000;
34+
const seconds = Math.floor((duration_ms / 1000) % 60);
35+
const minutes = Math.floor((duration_ms / (1000 * 60)) % 60);
36+
const hours = Math.floor((duration_ms / (1000 * 60 * 60)) % 24);
37+
const days = Math.floor(duration_ms / (1000 * 60 * 60 * 24));
38+
const parts = [];
39+
40+
if (days > 0) {
41+
parts.push(`${days}d`);
42+
}
43+
if (hours > 0) {
44+
parts.push(`${hours}h`);
45+
}
46+
if (minutes > 0) {
47+
parts.push(`${minutes}m`);
48+
}
49+
if (days == 0) {
50+
if (seconds > 0 || parts.length === 0) {
51+
parts.push(`${seconds}s`);
52+
}
53+
if (hours == 0 && minutes == 0) {
54+
if (milliseconds > 0) {
55+
parts.push(`${milliseconds}ms`);
56+
}
57+
}
58+
}
59+
60+
return parts.join(" ");
61+
}
62+
63+
// The input data is array of runs, where a single run contains many measurements (eg, file size,
64+
// build time).
65+
//
66+
// This function "transposes" the data, such that measurements with identical labels are merged to
67+
// form a single array which is what we want to plot.
68+
//
69+
// This doesn't depend on particular plotting library though.
70+
function batches_to_series(batches) {
71+
const results = new Map();
72+
for (const [index, batch] of batches.entries()) {
73+
for (const metric of batch.metrics) {
74+
if (!results.has(metric.name)) {
75+
results.set(metric.name, {
76+
name: metric.name,
77+
unit: undefined,
78+
value: [],
79+
git_commit: [],
80+
timestamp: [],
81+
});
82+
}
83+
84+
const series = results.get(metric.name);
85+
assert(series.name == metric.name);
86+
87+
if (series.unit) {
88+
assert(series.unit == metric.unit);
89+
} else {
90+
series.unit = metric.unit;
91+
}
92+
93+
// Even though our x-axis is time, we want to spread things out evenly by batch, rather than
94+
// group according to time. Apex charts is much quicker when given an x value, even though it
95+
// isn't strictly needed.
96+
series.value.push([batches.length - index, metric.value]);
97+
series.git_commit.push(batch.attributes.git_commit);
98+
series.timestamp.push(batch.timestamp);
99+
}
100+
}
101+
102+
return results;
103+
}
104+
105+
// Plot time series using <https://apexcharts.com>.
106+
function plot_series(series_list, root_node, batch_count) {
107+
for (const series of series_list.values()) {
108+
let options = {
109+
title: {
110+
text: series.name,
111+
},
112+
chart: {
113+
id: series.name,
114+
group: "devhub",
115+
type: "line",
116+
height: "400px",
117+
animations: {
118+
enabled: false,
119+
},
120+
events: {
121+
dataPointSelection: (event, chartContext, { dataPointIndex }) => {
122+
window.open(
123+
"https://github.com/mozilla/glean/commit/" +
124+
series.git_commit[dataPointIndex],
125+
);
126+
},
127+
},
128+
},
129+
markers: {
130+
size: 4,
131+
},
132+
series: [{
133+
name: series.name,
134+
data: series.value,
135+
}],
136+
xaxis: {
137+
categories: Array(series.value[series.value.length - 1][0]).fill("")
138+
.concat(
139+
series.timestamp.map((timestamp) =>
140+
format_date_day(new Date(timestamp * 1000))
141+
).reverse(),
142+
),
143+
min: 0,
144+
max: batch_count,
145+
tickAmount: 15,
146+
axisTicks: {
147+
show: false,
148+
},
149+
tooltip: {
150+
enabled: false,
151+
},
152+
},
153+
tooltip: {
154+
enabled: true,
155+
shared: false,
156+
intersect: true,
157+
x: {
158+
formatter: function (val, { dataPointIndex }) {
159+
const formattedDate = format_date_day_time(
160+
new Date(series.timestamp[dataPointIndex] * 1000),
161+
);
162+
return `<div>${
163+
series.git_commit[dataPointIndex]
164+
}</div><div>${formattedDate}</div>`;
165+
},
166+
},
167+
},
168+
};
169+
170+
if (series.unit === "bytes") {
171+
options.yaxis = {
172+
labels: {
173+
formatter: format_bytes,
174+
},
175+
};
176+
}
177+
178+
if (series.unit === "ms") {
179+
options.yaxis = {
180+
labels: {
181+
formatter: format_duration,
182+
},
183+
};
184+
}
185+
186+
const div = document.createElement("div");
187+
root_node.append(div);
188+
const chart = new ApexCharts(div, options);
189+
chart.render();
190+
}
191+
}
192+
193+
function format_bytes(bytes) {
194+
if (bytes === 0) return "0 Bytes";
195+
196+
const k = 1024;
197+
const sizes = [
198+
"Bytes",
199+
"KiB",
200+
"MiB",
201+
"GiB",
202+
"TiB",
203+
"PiB",
204+
"EiB",
205+
"ZiB",
206+
"YiB",
207+
];
208+
209+
let i = 0;
210+
while (i != sizes.length - 1 && Math.pow(k, i + 1) < bytes) {
211+
i += 1;
212+
}
213+
214+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
215+
}
216+
217+
function format_date_day(date) {
218+
return format_date(date, false);
219+
}
220+
221+
function format_date_day_time(date) {
222+
return format_date(date, true);
223+
}
224+
225+
function format_date(date, include_time) {
226+
assert(date instanceof Date);
227+
228+
const pad = (number) => String(number).padStart(2, "0");
229+
230+
const year = date.getFullYear();
231+
const month = pad(date.getMonth() + 1); // Months are 0-based.
232+
const day = pad(date.getDate());
233+
const hours = pad(date.getHours());
234+
const minutes = pad(date.getMinutes());
235+
const seconds = pad(date.getSeconds());
236+
return include_time
237+
? `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
238+
: `${year}-${month}-${day}`;
239+
}

tools/devhub/www/index.html

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Glean DevHub</title>
8+
<script src="./devhub.js"></script>
9+
<script src="apexcharts.min.js"></script>
10+
<link rel="stylesheet" href="style.css">
11+
</head>
12+
13+
<body>
14+
<nav>
15+
<h1>Glean DevHub</h1>
16+
</nav>
17+
18+
<main>
19+
<section id="metrics">
20+
<h2>Metrics
21+
<a href="https://github.com/mozilla/glean-devhubdb/tree/main/devhub">Raw data</a>
22+
</h2>
23+
<div id="charts"></div>
24+
</section>
25+
</main>
26+
27+
</body>
28+
29+
</html>

0 commit comments

Comments
 (0)