Skip to content

Commit ff33c5c

Browse files
committed
Deploy a devhub site
1 parent 3e234ac commit ff33c5c

File tree

5 files changed

+498
-0
lines changed

5 files changed

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

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)