mirror of
https://github.com/Noratrieb/does-it-build.git
synced 2026-01-14 18:35:01 +01:00
Cleanup and perf
This commit is contained in:
parent
61d78680e0
commit
79bb6d1eb0
6 changed files with 13 additions and 259 deletions
5
migrations/20250703173258_index.sql
Normal file
5
migrations/20250703173258_index.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- Add migration script here
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS build_info_nightly ON build_info (nightly);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS build_info_target ON build_info (target);
|
||||||
|
|
@ -100,13 +100,6 @@ impl Db {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn full_mega_monster(&self) -> Result<Vec<BuildInfo>> {
|
|
||||||
sqlx::query_as::<_, BuildInfo>("SELECT nightly, target, status, mode FROM build_info")
|
|
||||||
.fetch_all(&self.conn)
|
|
||||||
.await
|
|
||||||
.wrap_err("getting build status from DB")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn history_for_target(&self, target: &str) -> Result<Vec<BuildInfo>> {
|
pub async fn history_for_target(&self, target: &str) -> Result<Vec<BuildInfo>> {
|
||||||
sqlx::query_as::<_, BuildInfo>(
|
sqlx::query_as::<_, BuildInfo>(
|
||||||
"SELECT nightly, target, status, mode FROM build_info WHERE target = ?",
|
"SELECT nightly, target, status, mode FROM build_info WHERE target = ?",
|
||||||
|
|
|
||||||
24
src/web.rs
24
src/web.rs
|
|
@ -5,7 +5,7 @@ use axum::{
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
Json, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use color_eyre::{eyre::Context, Result};
|
use color_eyre::{eyre::Context, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -24,10 +24,7 @@ pub async fn webserver(db: Db) -> Result<()> {
|
||||||
.route("/build", get(web_build))
|
.route("/build", get(web_build))
|
||||||
.route("/target", get(web_target))
|
.route("/target", get(web_target))
|
||||||
.route("/nightly", get(web_nightly))
|
.route("/nightly", get(web_nightly))
|
||||||
.route("/full-table", get(web_full_table))
|
|
||||||
.route("/index.css", get(index_css))
|
.route("/index.css", get(index_css))
|
||||||
.route("/index.js", get(index_js))
|
|
||||||
.route("/full-mega-monster", get(full_mega_monster))
|
|
||||||
.with_state(AppState { db });
|
.with_state(AppState { db });
|
||||||
|
|
||||||
info!("Serving website on port 3000 (commit {})", crate::VERSION);
|
info!("Serving website on port 3000 (commit {})", crate::VERSION);
|
||||||
|
|
@ -240,9 +237,6 @@ async fn web_root(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn web_full_table() -> impl IntoResponse {
|
|
||||||
Html(include_str!("../static/full-table.html").replace("{{version}}", crate::VERSION))
|
|
||||||
}
|
|
||||||
async fn index_css() -> impl IntoResponse {
|
async fn index_css() -> impl IntoResponse {
|
||||||
(
|
(
|
||||||
[(
|
[(
|
||||||
|
|
@ -252,22 +246,6 @@ async fn index_css() -> impl IntoResponse {
|
||||||
include_str!("../static/index.css"),
|
include_str!("../static/index.css"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
async fn index_js() -> impl IntoResponse {
|
|
||||||
(
|
|
||||||
[(
|
|
||||||
axum::http::header::CONTENT_TYPE,
|
|
||||||
axum::http::HeaderValue::from_static("text/javascript"),
|
|
||||||
)],
|
|
||||||
include_str!("../static/index.js"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn full_mega_monster(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
state.db.full_mega_monster().await.map(Json).map_err(|err| {
|
|
||||||
error!(?err, "Error loading target state");
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct TriggerBuildBody {
|
struct TriggerBuildBody {
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Does it build?</title>
|
|
||||||
<link rel="stylesheet" href="index.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Does it build?</h1>
|
|
||||||
<p>This website builds every rustc target on many nightlies to check which ones work and which ones do not.</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#core-build">Core build</a></li>
|
|
||||||
<li><a href="#std-check-build">Std check build</a></li>
|
|
||||||
</ul>
|
|
||||||
<!--core-->
|
|
||||||
<h2 id="core-build">Core Build</h2>
|
|
||||||
<p>Builds every target with:
|
|
||||||
<pre>cargo build --release -Zbuild-std=core</pre></p>
|
|
||||||
<p>This checks that codegen/linking of core works, but does not check whether std builds.</p>
|
|
||||||
<label for="target-filter">Target Filter</label>
|
|
||||||
<input id="target-filter" type="search" />
|
|
||||||
<label for="target-filter-failed">Filter failed</label>
|
|
||||||
<input type="checkbox" id="target-filter-failed" />
|
|
||||||
|
|
||||||
<table id="target-state" class="target-state-table">
|
|
||||||
<tr>
|
|
||||||
<td>loading...</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!--std-->
|
|
||||||
<h2 id="std-check-build">Std Check Build</h2>
|
|
||||||
<p>Builds every target with:
|
|
||||||
<pre>cargo miri setup</pre></p>
|
|
||||||
<p>This checks that std builds (on targets that have it) but does not check whether codegen/linking works.</p>
|
|
||||||
<label for="target-filter-miri">Target Filter</label>
|
|
||||||
<input id="target-filter-miri" type="search" />
|
|
||||||
<label for="target-filter-failed-miri">Filter failed</label>
|
|
||||||
<input type="checkbox" id="target-filter-failed-miri" />
|
|
||||||
|
|
||||||
<table id="target-state-miri" class="target-state-table">
|
|
||||||
<tr>
|
|
||||||
<td>loading...</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<footer class="footer">
|
|
||||||
<span>does-it-build {{version}}</span>
|
|
||||||
<a href="https://github.com/Noratrieb/does-it-build">
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
aria-labelledby="github-logo-title"
|
|
||||||
>
|
|
||||||
<title id="github-logo-title">GitHub</title>
|
|
||||||
<path
|
|
||||||
fill="black"
|
|
||||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
<script src="index.js">
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
159
static/index.js
159
static/index.js
|
|
@ -1,159 +0,0 @@
|
||||||
class Table {
|
|
||||||
constructor(data, tableElemId, filterElemId, filterFailedElemId) {
|
|
||||||
this.data = data;
|
|
||||||
this.elem = document.getElementById(tableElemId);
|
|
||||||
|
|
||||||
document.getElementById(filterElemId)?.addEventListener("input", (e) => {
|
|
||||||
this.filter.search = e.target.value;
|
|
||||||
this.render();
|
|
||||||
});
|
|
||||||
document
|
|
||||||
.getElementById(filterFailedElemId)
|
|
||||||
?.addEventListener("input", (e) => {
|
|
||||||
this.filter.filterFailed = e.target.checked;
|
|
||||||
this.render();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.filter = {
|
|
||||||
search: "",
|
|
||||||
filterFailed: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
update(data) {
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const allTargets = new Set();
|
|
||||||
const allNightlies = new Set();
|
|
||||||
|
|
||||||
// The infos grouped by target.
|
|
||||||
const targetInfos = new Map();
|
|
||||||
|
|
||||||
// Targets that have, at some point, errored
|
|
||||||
const targetsWithErrors = new Set();
|
|
||||||
|
|
||||||
// Whether a nightly is completely broken.
|
|
||||||
// These are still filtered out when filter failed is selected.
|
|
||||||
const isNightlyBroken = new Map();
|
|
||||||
|
|
||||||
// The first pass over the data, to find nightlies that are broken.
|
|
||||||
for (const info of this.data) {
|
|
||||||
if (!isNightlyBroken.has(info.nightly)) {
|
|
||||||
// Assume that a nightly is broken until proven otherwise.
|
|
||||||
isNightlyBroken.set(info.nightly, true);
|
|
||||||
}
|
|
||||||
if (info.status == "pass") {
|
|
||||||
// This nightly has built something, so it's clearly not broken :).
|
|
||||||
isNightlyBroken.set(info.nightly, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second pass over the data, group by nightly and prepare data for filter.
|
|
||||||
for (const info of this.data) {
|
|
||||||
allNightlies.add(info.nightly);
|
|
||||||
|
|
||||||
if (!info.target.includes(this.filter.search)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.status === "error" && !isNightlyBroken.get(info.nightly)) {
|
|
||||||
targetsWithErrors.add(info.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
allTargets.add(info.target);
|
|
||||||
if (!targetInfos.has(info.target)) {
|
|
||||||
targetInfos.set(info.target, new Map());
|
|
||||||
}
|
|
||||||
targetInfos.get(info.target).set(info.nightly, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nightlies = Array.from(allNightlies);
|
|
||||||
nightlies.sort();
|
|
||||||
nightlies.reverse();
|
|
||||||
const targets = Array.from(allTargets);
|
|
||||||
targets.sort();
|
|
||||||
|
|
||||||
const header = document.createElement("tr");
|
|
||||||
const headerTarget = document.createElement("th");
|
|
||||||
headerTarget.innerText = "target";
|
|
||||||
header.appendChild(headerTarget);
|
|
||||||
nightlies.forEach((target) => {
|
|
||||||
const elem = document.createElement("th");
|
|
||||||
elem.classList.add("target-header");
|
|
||||||
elem.innerText = target;
|
|
||||||
header.appendChild(elem);
|
|
||||||
});
|
|
||||||
|
|
||||||
const rows = targets.flatMap((target) => {
|
|
||||||
if (this.filter.filterFailed && !targetsWithErrors.has(target)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const tr = document.createElement("tr");
|
|
||||||
|
|
||||||
const targetCol = document.createElement("td");
|
|
||||||
targetCol.innerText = target;
|
|
||||||
targetCol.classList.add("target-name-col");
|
|
||||||
tr.appendChild(targetCol);
|
|
||||||
|
|
||||||
const info = targetInfos.get(target) ?? new Map();
|
|
||||||
|
|
||||||
for (const nightly of nightlies) {
|
|
||||||
const td = document.createElement("td");
|
|
||||||
const targetInfo = info.get(nightly);
|
|
||||||
|
|
||||||
if (targetInfo) {
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.classList.add("build-info-a");
|
|
||||||
a.href = `build?nightly=${encodeURIComponent(
|
|
||||||
nightly
|
|
||||||
)}&target=${encodeURIComponent(target)}&mode=${encodeURIComponent(
|
|
||||||
targetInfo.mode
|
|
||||||
)}`;
|
|
||||||
a.innerText = targetInfo.status == "pass" ? "✅" : "❌";
|
|
||||||
td.appendChild(a);
|
|
||||||
td.classList.add("build-cell");
|
|
||||||
td.classList.add(targetInfo.status);
|
|
||||||
} else {
|
|
||||||
td.innerText = "";
|
|
||||||
td.classList.add("missing");
|
|
||||||
}
|
|
||||||
tr.appendChild(td);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [tr];
|
|
||||||
});
|
|
||||||
this.elem.replaceChildren(header, ...rows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const coreTable = new Table(
|
|
||||||
[],
|
|
||||||
"target-state",
|
|
||||||
"target-filter",
|
|
||||||
"target-filter-failed"
|
|
||||||
);
|
|
||||||
const miriTable = new Table(
|
|
||||||
[],
|
|
||||||
"target-state-miri",
|
|
||||||
"target-filter-miri",
|
|
||||||
"target-filter-failed-miri"
|
|
||||||
);
|
|
||||||
|
|
||||||
function fetchTargets() {
|
|
||||||
fetch("full-mega-monster")
|
|
||||||
.then((body) => body.json())
|
|
||||||
.then((body) => {
|
|
||||||
const core = body.filter((info) => info.mode === "core");
|
|
||||||
const miri = body.filter((info) => info.mode === "miri-std");
|
|
||||||
coreTable.update(core);
|
|
||||||
miriTable.update(miri);
|
|
||||||
coreTable.render();
|
|
||||||
miriTable.render();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial fetch
|
|
||||||
fetchTargets();
|
|
||||||
|
|
@ -23,7 +23,13 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>Nightlies</h1>
|
<h1>Nightlies</h1>
|
||||||
<ul></ul>
|
<ul>
|
||||||
|
{% for nightly in nightlies.iter().take(5) %}
|
||||||
|
<li>
|
||||||
|
<a href="nightly?nightly={{ nightly }}">{{ nightly }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
To view a list of all nightlies, check
|
To view a list of all nightlies, check
|
||||||
<a href="#all-nightlies">the list at the end</a>.
|
<a href="#all-nightlies">the list at the end</a>.
|
||||||
|
|
@ -68,6 +74,5 @@
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="index.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue