mirror of
https://github.com/Noratrieb/does-it-build.git
synced 2026-01-14 18:35:01 +01:00
Compare commits
No commits in common. "d15a7465584b5e90dc19126cc3d097683d055a63" and "25179d488df81b2d585970440b3f716717e9a8f4" have entirely different histories.
d15a746558
...
25179d488d
13 changed files with 119 additions and 261 deletions
64
Cargo.lock
generated
64
Cargo.lock
generated
|
|
@ -238,15 +238,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
|
|
@ -457,16 +448,13 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"askama",
|
||||
"axum",
|
||||
"bs58",
|
||||
"color-eyre",
|
||||
"futures",
|
||||
"jiff",
|
||||
"jsonwebtoken",
|
||||
"octocrab",
|
||||
"reqwest",
|
||||
"rootcause",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sqlx",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
|
|
@ -557,7 +545,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
|||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"spin 0.9.8",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1169,7 +1157,7 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
dependencies = [
|
||||
"spin 0.9.8",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1803,30 +1791,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rootcause"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12e6b4d9966d17c1e58cd682656e9e1551f19f421b07d7618723009445f06099"
|
||||
dependencies = [
|
||||
"hashbrown 0.16.0",
|
||||
"indexmap",
|
||||
"rootcause-internals",
|
||||
"rustc-hash",
|
||||
"spin 0.10.0",
|
||||
"triomphe",
|
||||
"unsize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rootcause-internals"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac64da7400123d25e3603dfd91a0ce7a78a3ad6e2d9400e6cdbb1aef0574c0d7"
|
||||
dependencies = [
|
||||
"triomphe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.8"
|
||||
|
|
@ -2168,12 +2132,6 @@ dependencies = [
|
|||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
|
|
@ -2706,15 +2664,6 @@ dependencies = [
|
|||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39"
|
||||
dependencies = [
|
||||
"unsize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
|
|
@ -2754,15 +2703,6 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
||||
|
||||
[[package]]
|
||||
name = "unsize"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fa7a7a734c1a5664a662ddcea0b6c9472a21da8888c957c7f1eaa09dba7a939"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ axum = { version = "0.8.6", default-features = false, features = [
|
|||
"tracing",
|
||||
"macros",
|
||||
] }
|
||||
color-eyre = "0.6.3"
|
||||
futures = "0.3.30"
|
||||
jiff = "0.2.16"
|
||||
jsonwebtoken = { version = "9.3.1" }
|
||||
|
|
@ -22,7 +23,6 @@ reqwest = { version = "0.12.7", features = [
|
|||
"rustls-tls",
|
||||
"http2",
|
||||
], default-features = false }
|
||||
rootcause = "0.10.0"
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
sqlx = { version = "0.8.2", features = ["runtime-tokio", "sqlite"] }
|
||||
tempfile = "3.12.0"
|
||||
|
|
@ -31,6 +31,4 @@ tracing = { version = "0.1.40", features = ["attributes"] }
|
|||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
||||
[build-dependencies]
|
||||
bs58 = "0.5.1"
|
||||
color-eyre = "0.6.3"
|
||||
sha2 = "0.10.9"
|
||||
|
|
|
|||
32
build.rs
32
build.rs
|
|
@ -1,29 +1,6 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
fn main() {
|
||||
// Always rerun.
|
||||
|
||||
let index_css = include_str!("static/index.css");
|
||||
let index_js = include_str!("static/index.js");
|
||||
|
||||
let index_css_name = get_file_ref("css", index_css);
|
||||
let index_js_name = get_file_ref("js", index_js);
|
||||
|
||||
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
|
||||
std::fs::write(
|
||||
out_dir.join("revs.rs"),
|
||||
format!(
|
||||
r#"
|
||||
pub const INDEX_CSS_NAME: &str = "{index_css_name}";
|
||||
pub const INDEX_JS_NAME: &str = "{index_js_name}";
|
||||
"#
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let version = if let Ok(commit) = try_get_commit() {
|
||||
match has_no_changes() {
|
||||
Ok(true) => commit,
|
||||
|
|
@ -43,15 +20,6 @@ pub const INDEX_JS_NAME: &str = "{index_js_name}";
|
|||
println!("cargo:rustc-env=GIT_COMMIT_SHORT={version_short}");
|
||||
}
|
||||
|
||||
fn get_file_ref(ext: &str, content: &str) -> String {
|
||||
let mut hash = sha2::Sha256::new();
|
||||
hash.update(content);
|
||||
let digest = hash.finalize();
|
||||
let rev = bs58::encode(digest).into_string();
|
||||
let rev = &rev[..16];
|
||||
format!("/{rev}.{ext}")
|
||||
}
|
||||
|
||||
fn try_get_commit() -> color_eyre::Result<String> {
|
||||
if let Ok(overridden) = std::env::var("DOES_IT_BUILD_OVERRIDE_VERSION") {
|
||||
return Ok(overridden);
|
||||
|
|
|
|||
68
src/build.rs
68
src/build.rs
|
|
@ -6,8 +6,11 @@ use std::{
|
|||
time::{Duration, Instant, SystemTime},
|
||||
};
|
||||
|
||||
use color_eyre::{
|
||||
eyre::{bail, Context},
|
||||
Result,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use rootcause::{prelude::ResultExt, report};
|
||||
use tokio::process::Command;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
|
|
@ -15,7 +18,6 @@ use crate::{
|
|||
db::{BuildMode, Db, FullBuildInfo, Status},
|
||||
nightlies::Nightlies,
|
||||
notification::GitHubClient,
|
||||
Result,
|
||||
};
|
||||
|
||||
struct CustomBuildFlags {
|
||||
|
|
@ -71,11 +73,11 @@ pub async fn background_builder(db: Db, github_client: GitHubClient) -> Result<(
|
|||
}
|
||||
|
||||
async fn background_builder_inner(db: &Db, github_client: &GitHubClient) -> Result<()> {
|
||||
let nightlies = Nightlies::fetch().await.context("fetching nightlies")?;
|
||||
let nightlies = Nightlies::fetch().await.wrap_err("fetching nightlies")?;
|
||||
let already_finished = db
|
||||
.finished_nightlies()
|
||||
.await
|
||||
.context("fetching finished nightlies")?;
|
||||
.wrap_err("fetching finished nightlies")?;
|
||||
|
||||
let next = nightlies.select_latest_to_build(&already_finished);
|
||||
match next {
|
||||
|
|
@ -83,12 +85,12 @@ async fn background_builder_inner(db: &Db, github_client: &GitHubClient) -> Resu
|
|||
info!(%nightly, %mode, "Building next nightly");
|
||||
let result = build_every_target_for_toolchain(db, &nightly, mode, github_client)
|
||||
.await
|
||||
.context_with(|| format!("building targets for toolchain {nightly}"));
|
||||
.wrap_err_with(|| format!("building targets for toolchain {nightly}"));
|
||||
if let Err(err) = result {
|
||||
error!(%nightly, %mode, ?err, "Failed to build nightly");
|
||||
db.finish_nightly_as_broken(&nightly, mode, &format!("{err:?}"))
|
||||
.await
|
||||
.context("marking nightly as broken")?;
|
||||
.wrap_err("marking nightly as broken")?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
|
@ -106,16 +108,16 @@ async fn targets_for_toolchain(toolchain: &Toolchain) -> Result<Vec<String>> {
|
|||
.arg("target-list")
|
||||
.output()
|
||||
.await
|
||||
.context("failed to spawn rustc")?;
|
||||
.wrap_err("failed to spawn rustc")?;
|
||||
if !output.status.success() {
|
||||
return Err(report!(
|
||||
bail!(
|
||||
"failed to get target-list from rustc: {:?}",
|
||||
String::from_utf8(output.stderr)
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(output.stdout)
|
||||
.context("rustc target-list is invalid UTF-8")?
|
||||
.wrap_err("rustc target-list is invalid UTF-8")?
|
||||
.split_whitespace()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect())
|
||||
|
|
@ -133,12 +135,9 @@ async fn install_toolchain(toolchain: &Toolchain, mode: BuildMode) -> Result<()>
|
|||
.arg("minimal")
|
||||
.output()
|
||||
.await
|
||||
.context("failed to spawn rustup")?;
|
||||
.wrap_err("failed to spawn rustup")?;
|
||||
if !result.status.success() {
|
||||
return Err(report!(
|
||||
"rustup failed: {:?}",
|
||||
String::from_utf8(result.stderr)
|
||||
));
|
||||
bail!("rustup failed: {:?}", String::from_utf8(result.stderr));
|
||||
}
|
||||
let result = Command::new("rustup")
|
||||
.arg("component")
|
||||
|
|
@ -148,12 +147,9 @@ async fn install_toolchain(toolchain: &Toolchain, mode: BuildMode) -> Result<()>
|
|||
.arg(&toolchain.0)
|
||||
.output()
|
||||
.await
|
||||
.context("failed to spawn rustup")?;
|
||||
.wrap_err("failed to spawn rustup")?;
|
||||
if !result.status.success() {
|
||||
return Err(report!(
|
||||
"rustup failed: {:?}",
|
||||
String::from_utf8(result.stderr)
|
||||
));
|
||||
bail!("rustup failed: {:?}", String::from_utf8(result.stderr));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -169,12 +165,12 @@ async fn uninstall_toolchain(toolchain: &Toolchain) -> Result<()> {
|
|||
.arg(&toolchain.0)
|
||||
.output()
|
||||
.await
|
||||
.context("failed to spawn rustup")?;
|
||||
.wrap_err("failed to spawn rustup")?;
|
||||
if !result.status.success() {
|
||||
return Err(report!(
|
||||
bail!(
|
||||
"rustup toolchain remove failed: {:?}",
|
||||
String::from_utf8(result.stderr)
|
||||
));
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -195,7 +191,7 @@ pub async fn build_every_target_for_toolchain(
|
|||
|
||||
let targets = targets_for_toolchain(&toolchain)
|
||||
.await
|
||||
.context("failed to get targets")?;
|
||||
.wrap_err("failed to get targets")?;
|
||||
|
||||
let results = futures::stream::iter(
|
||||
targets
|
||||
|
|
@ -212,7 +208,7 @@ pub async fn build_every_target_for_toolchain(
|
|||
for target in targets {
|
||||
build_single_target(db, nightly, &target, mode, github_client)
|
||||
.await
|
||||
.context_with(|| format!("building target {target} for toolchain {toolchain}"))?;
|
||||
.wrap_err_with(|| format!("building target {target} for toolchain {toolchain}"))?;
|
||||
}
|
||||
|
||||
// Mark it as finished, so we never have to build it again.
|
||||
|
|
@ -234,7 +230,7 @@ async fn build_single_target(
|
|||
let existing = db
|
||||
.build_status_full(nightly, target, mode)
|
||||
.await
|
||||
.context("getting existing build")?;
|
||||
.wrap_err("getting existing build")?;
|
||||
if existing.is_some() {
|
||||
debug!("Build already exists");
|
||||
return Ok(());
|
||||
|
|
@ -242,13 +238,13 @@ async fn build_single_target(
|
|||
|
||||
info!("Building target");
|
||||
|
||||
let tmpdir = tempfile::tempdir().context("creating temporary directory")?;
|
||||
let tmpdir = tempfile::tempdir().wrap_err("creating temporary directory")?;
|
||||
|
||||
let start_time = Instant::now();
|
||||
|
||||
let result = build_target(tmpdir.path(), &Toolchain::from_nightly(nightly), target)
|
||||
.await
|
||||
.context("running build")?;
|
||||
.wrap_err("running build")?;
|
||||
|
||||
let full_build_info = FullBuildInfo {
|
||||
nightly: nightly.into(),
|
||||
|
|
@ -302,14 +298,14 @@ async fn build_target(tmpdir: &Path, toolchain: &Toolchain, target: &str) -> Res
|
|||
.current_dir(tmpdir)
|
||||
.output()
|
||||
.await
|
||||
.context("spawning cargo init")?;
|
||||
.wrap_err("spawning cargo init")?;
|
||||
if !init.status.success() {
|
||||
return Err(report!("init failed: {}", String::from_utf8(init.stderr)?));
|
||||
bail!("init failed: {}", String::from_utf8(init.stderr)?);
|
||||
}
|
||||
|
||||
let librs = tmpdir.join("src").join("lib.rs");
|
||||
std::fs::write(&librs, "#![no_std]\n")
|
||||
.context_with(|| format!("writing to {}", librs.display()))?;
|
||||
.wrap_err_with(|| format!("writing to {}", librs.display()))?;
|
||||
|
||||
async fn run(
|
||||
toolchain: &Toolchain,
|
||||
|
|
@ -334,16 +330,14 @@ async fn build_target(tmpdir: &Path, toolchain: &Toolchain, target: &str) -> Res
|
|||
*rustflags = Some(flags);
|
||||
}
|
||||
|
||||
let output = cmd
|
||||
.current_dir(tmpdir)
|
||||
cmd.current_dir(tmpdir)
|
||||
.output()
|
||||
.await
|
||||
.context("spawning cargo build")?;
|
||||
Ok(output)
|
||||
.wrap_err("spawning cargo build")
|
||||
}
|
||||
|
||||
let mut output = run(toolchain, target, &mut rustflags, tmpdir, "-Zbuild-std").await?;
|
||||
let mut stderr = String::from_utf8(output.stderr).context("cargo stderr utf8")?;
|
||||
let mut stderr = String::from_utf8(output.stderr).wrap_err("cargo stderr utf8")?;
|
||||
|
||||
let status = if output.status.success() {
|
||||
Status::Pass
|
||||
|
|
@ -358,7 +352,7 @@ async fn build_target(tmpdir: &Path, toolchain: &Toolchain, target: &str) -> Res
|
|||
"-Zbuild-std=core",
|
||||
)
|
||||
.await?;
|
||||
stderr = String::from_utf8(output.stderr).context("cargo stderr utf8")?;
|
||||
stderr = String::from_utf8(output.stderr).wrap_err("cargo stderr utf8")?;
|
||||
|
||||
if output.status.success() {
|
||||
Status::Pass
|
||||
|
|
|
|||
78
src/db.rs
78
src/db.rs
|
|
@ -1,11 +1,12 @@
|
|||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use rootcause::{prelude::ResultExt, report};
|
||||
use color_eyre::{
|
||||
eyre::{bail, Context},
|
||||
Result,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{migrate::Migrator, sqlite::SqliteConnectOptions, Pool, Sqlite};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Db {
|
||||
pub conn: Pool<Sqlite>,
|
||||
|
|
@ -111,13 +112,12 @@ pub struct NotificationIssue {
|
|||
impl Db {
|
||||
pub async fn open(path: &str) -> Result<Self> {
|
||||
let db_opts = SqliteConnectOptions::from_str(path)
|
||||
.context("parsing database URL")
|
||||
.attach(format!("url: {path}"))?
|
||||
.wrap_err("parsing database URL")?
|
||||
.create_if_missing(true);
|
||||
|
||||
let conn = Pool::connect_with(db_opts)
|
||||
.await
|
||||
.context_with(|| format!("opening db from `{path}`"))?;
|
||||
.wrap_err_with(|| format!("opening db from `{path}`"))?;
|
||||
Ok(Self { conn })
|
||||
}
|
||||
|
||||
|
|
@ -137,44 +137,38 @@ impl Db {
|
|||
.bind(info.build_duration_ms)
|
||||
.execute(&self.conn)
|
||||
.await
|
||||
.context("inserting build info into database")?;
|
||||
.wrap_err("inserting build info into database")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn history_for_target(&self, target: &str) -> Result<Vec<BuildInfo>> {
|
||||
let history = sqlx::query_as::<_, BuildInfo>(
|
||||
sqlx::query_as::<_, BuildInfo>(
|
||||
"SELECT nightly, target, status, mode FROM build_info WHERE target = ?",
|
||||
)
|
||||
.bind(target)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting history for single target")
|
||||
.attach(format!("target: {target}"))?;
|
||||
Ok(history)
|
||||
.wrap_err("getting history for single target")
|
||||
}
|
||||
|
||||
pub async fn history_for_nightly(&self, nightly: &str) -> Result<Vec<BuildInfo>> {
|
||||
let history = sqlx::query_as::<_, BuildInfo>(
|
||||
sqlx::query_as::<_, BuildInfo>(
|
||||
"SELECT nightly, target, status, mode FROM build_info WHERE nightly = ?",
|
||||
)
|
||||
.bind(nightly)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting history for single nightly")
|
||||
.attach(format!("nightly: {nightly}"))?;
|
||||
Ok(history)
|
||||
.wrap_err("getting history for single nightly")
|
||||
}
|
||||
|
||||
pub async fn nightly_info(&self, nightly: &str) -> Result<Vec<FinishedNightlyWithBroken>> {
|
||||
let info = sqlx::query_as::<_, FinishedNightlyWithBroken>(
|
||||
sqlx::query_as::<_, FinishedNightlyWithBroken>(
|
||||
"SELECT nightly, mode, is_broken, broken_error FROM finished_nightly WHERE nightly = ?",
|
||||
)
|
||||
.bind(nightly)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting finished_nightly for single nightly")
|
||||
.attach(format!("nightly: {nightly}"))?;
|
||||
Ok(info)
|
||||
.wrap_err("getting finished_nightly for single nightly")
|
||||
}
|
||||
|
||||
pub async fn target_list(&self) -> Result<Vec<String>> {
|
||||
|
|
@ -183,14 +177,11 @@ impl Db {
|
|||
target: String,
|
||||
}
|
||||
|
||||
let list = sqlx::query_as::<_, TargetName>(
|
||||
"SELECT DISTINCT target FROM build_info ORDER BY target",
|
||||
)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting list of all targets")?;
|
||||
|
||||
Ok(list.into_iter().map(|elem| elem.target).collect())
|
||||
sqlx::query_as::<_, TargetName>("SELECT DISTINCT target FROM build_info ORDER BY target")
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.wrap_err("getting list of all targets")
|
||||
.map(|elems| elems.into_iter().map(|elem| elem.target).collect())
|
||||
}
|
||||
|
||||
pub async fn nightly_list(&self) -> Result<Vec<String>> {
|
||||
|
|
@ -199,14 +190,13 @@ impl Db {
|
|||
nightly: String,
|
||||
}
|
||||
|
||||
let list = sqlx::query_as::<_, NightlyName>(
|
||||
sqlx::query_as::<_, NightlyName>(
|
||||
"SELECT DISTINCT nightly FROM build_info ORDER BY nightly DESC",
|
||||
)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting list of all nightlies")?;
|
||||
|
||||
Ok(list.into_iter().map(|elem| elem.nightly).collect())
|
||||
.wrap_err("getting list of all targets")
|
||||
.map(|elems| elems.into_iter().map(|elem| elem.nightly).collect())
|
||||
}
|
||||
|
||||
pub async fn build_count(&self) -> Result<BuildStats> {
|
||||
|
|
@ -221,7 +211,7 @@ impl Db {
|
|||
)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting total count of builds")?;
|
||||
.wrap_err("getting list of all targets")?;
|
||||
|
||||
let count = |status| {
|
||||
results
|
||||
|
|
@ -252,8 +242,7 @@ impl Db {
|
|||
.bind(mode)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting build status from DB")
|
||||
.attach(format!("nightly: {nightly}, target: {target}"))?;
|
||||
.wrap_err("getting build status from DB")?;
|
||||
Ok(result.first().cloned())
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +251,7 @@ impl Db {
|
|||
sqlx::query_as::<_, FinishedNightly>("SELECT nightly, mode from finished_nightly")
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("getting finished nightlies")?;
|
||||
.wrap_err("fetching finished nightlies")?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
|
@ -275,10 +264,10 @@ impl Db {
|
|||
.bind(mode)
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.context("checking whether a nightly is finished")?;
|
||||
.wrap_err("checking whether a nightly is finished")?;
|
||||
|
||||
if result.len() > 1 {
|
||||
return Err(report!("found more than one result for {nightly} {mode}"));
|
||||
bail!("found more than one result for {nightly} {mode}");
|
||||
}
|
||||
|
||||
Ok(result.len() == 1)
|
||||
|
|
@ -290,7 +279,7 @@ impl Db {
|
|||
.bind(mode)
|
||||
.execute(&self.conn)
|
||||
.await
|
||||
.context("inserting finished nightly")?;
|
||||
.wrap_err("inserting finished nightly")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -306,7 +295,7 @@ impl Db {
|
|||
.bind(error)
|
||||
.execute(&self.conn)
|
||||
.await
|
||||
.context("inserting finished broken nightly")?;
|
||||
.wrap_err("inserting finished broken nightly")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -314,14 +303,13 @@ impl Db {
|
|||
&self,
|
||||
target: &str,
|
||||
) -> Result<Option<NotificationIssue>> {
|
||||
let notification = sqlx::query_as::<_, NotificationIssue>(
|
||||
sqlx::query_as::<_, NotificationIssue>(
|
||||
"SELECT * FROM notification_issues WHERE status = 'open' AND target = ?",
|
||||
)
|
||||
.bind(target)
|
||||
.fetch_optional(&self.conn)
|
||||
.await
|
||||
.context("finding existing notification")?;
|
||||
Ok(notification)
|
||||
.wrap_err("finding existing notification")
|
||||
}
|
||||
|
||||
pub async fn insert_notification(&self, notification: NotificationIssue) -> Result<()> {
|
||||
|
|
@ -337,7 +325,7 @@ impl Db {
|
|||
.bind(notification.last_update_date)
|
||||
.execute(&self.conn)
|
||||
.await
|
||||
.context("inserting new notification")?;
|
||||
.wrap_err("inserting new notification")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -347,7 +335,7 @@ impl Db {
|
|||
.bind(issue_number)
|
||||
.execute(&self.conn)
|
||||
.await
|
||||
.context("marking notification as closed")?;
|
||||
.wrap_err("marking notification as closed")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +349,7 @@ impl Db {
|
|||
.bind(issue_number)
|
||||
.execute(&self.conn)
|
||||
.await
|
||||
.context("marking notification as closed")?;
|
||||
.wrap_err("marking notification as closed")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18
src/main.rs
18
src/main.rs
|
|
@ -4,15 +4,13 @@ mod nightlies;
|
|||
mod notification;
|
||||
mod web;
|
||||
|
||||
use color_eyre::{eyre::WrapErr, Result};
|
||||
use db::Db;
|
||||
use rootcause::{prelude::ResultExt, Report};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
const VERSION: &str = env!("GIT_COMMIT");
|
||||
const VERSION_SHORT: &str = env!("GIT_COMMIT_SHORT");
|
||||
|
||||
type Result<T, E = Report> = std::result::Result<T, E>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
main_inner().await
|
||||
|
|
@ -27,25 +25,25 @@ async fn main_inner() -> Result<()> {
|
|||
db::MIGRATOR
|
||||
.run(&db.conn)
|
||||
.await
|
||||
.context("running migrations")?;
|
||||
.wrap_err("running migrations")?;
|
||||
|
||||
let send_pings = std::env::var("GITHUB_SEND_PINGS")
|
||||
.map(|_| true)
|
||||
.unwrap_or(false);
|
||||
let github_owner = std::env::var("GITHUB_OWNER").context("missing GITHUB_OWNER env var")?;
|
||||
let github_repo = std::env::var("GITHUB_REPO").context("missing GITHUB_REPO env var")?;
|
||||
let github_owner = std::env::var("GITHUB_OWNER").wrap_err("missing GITHUB_OWNER env var")?;
|
||||
let github_repo = std::env::var("GITHUB_REPO").wrap_err("missing GITHUB_REPO env var")?;
|
||||
let app_id = std::env::var("GITHUB_APP_ID")
|
||||
.context("missing GITHUB_APP_ID env var")?
|
||||
.wrap_err("missing GITHUB_APP_ID env var")?
|
||||
.parse::<u64>()
|
||||
.context("invalid GITHUB_APP_ID")?;
|
||||
.wrap_err("invalid GITHUB_APP_ID")?;
|
||||
let key = std::env::var("GITHUB_APP_PRIVATE_KEY")
|
||||
.context("missing GITHUB_APP_PRIVATE_KEY env var")?;
|
||||
.wrap_err("missing GITHUB_APP_PRIVATE_KEY env var")?;
|
||||
let key = jsonwebtoken::EncodingKey::from_rsa_pem(key.as_bytes()).unwrap();
|
||||
|
||||
let github_client = octocrab::Octocrab::builder()
|
||||
.app(app_id.into(), key)
|
||||
.build()
|
||||
.context("failed to create client")?;
|
||||
.wrap_err("failed to create client")?;
|
||||
|
||||
let github_client = notification::GitHubClient::new(
|
||||
send_pings,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::HashSet;
|
||||
use std::hash::RandomState;
|
||||
|
||||
use rootcause::prelude::ResultExt;
|
||||
use color_eyre::eyre::Context;
|
||||
use color_eyre::Result;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::db::{BuildMode, FinishedNightly};
|
||||
use crate::Result;
|
||||
|
||||
const EARLIEST_CUTOFF_DATE: &str = "2023-01-01";
|
||||
|
||||
|
|
@ -15,21 +15,13 @@ pub struct Nightlies {
|
|||
}
|
||||
|
||||
impl Nightlies {
|
||||
async fn get_body(url: &str) -> Result<String, rootcause::Report> {
|
||||
Ok(reqwest::get(url)
|
||||
pub async fn fetch() -> Result<Nightlies> {
|
||||
let manifests = reqwest::get("https://static.rust-lang.org/manifests.txt")
|
||||
.await
|
||||
.context("executing GET request")?
|
||||
.wrap_err("fetching https://static.rust-lang.org/manifests.txt")?
|
||||
.text()
|
||||
.await
|
||||
.context("fetching body")?)
|
||||
}
|
||||
|
||||
pub async fn fetch() -> Result<Nightlies> {
|
||||
let url = "https://static.rust-lang.org/manifests.txt";
|
||||
let manifests = Nightlies::get_body(url)
|
||||
.await
|
||||
.context(format!("Fetching manifests.txt"))
|
||||
.attach(format!("url: {url}"))?;
|
||||
.wrap_err("fetching body of https://static.rust-lang.org/manifests.txt")?;
|
||||
let mut all = nightlies_from_manifest(&manifests)
|
||||
.into_iter()
|
||||
.filter(|date| date.as_str() > EARLIEST_CUTOFF_DATE)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use color_eyre::eyre::{Context, Result};
|
||||
use octocrab::models::issues::IssueStateReason;
|
||||
use octocrab::models::IssueState;
|
||||
use rootcause::prelude::ResultExt;
|
||||
use tracing::info;
|
||||
|
||||
use crate::db::{Db, FullBuildInfo, NotificationIssue, NotificationStatus, Status};
|
||||
use crate::Result;
|
||||
|
||||
pub const TABLE_FILE: &str = file!();
|
||||
pub const TABLE_LINE: u32 = line!() + 1;
|
||||
|
|
@ -56,11 +55,11 @@ impl GitHubClient {
|
|||
.apps()
|
||||
.get_repository_installation(&owner, &repo)
|
||||
.await
|
||||
.context_with(|| format!("getting installation for {owner}/{repo}"))?;
|
||||
.wrap_err_with(|| format!("getting installation for {owner}/{repo}"))?;
|
||||
|
||||
let client = client
|
||||
.installation(installation.id)
|
||||
.context("getting client for installation")?;
|
||||
.wrap_err("getting client for installation")?;
|
||||
|
||||
Ok(Self {
|
||||
send_pings,
|
||||
|
|
@ -144,11 +143,11 @@ This update is sent after a month of inactivity.
|
|||
),
|
||||
)
|
||||
.await
|
||||
.context("creating update comment")?;
|
||||
.wrap_err("creating update comment")?;
|
||||
|
||||
db.set_notification_last_update(issue.issue_number, jiff::Timestamp::now())
|
||||
.await
|
||||
.context("updating last_update_date in DB")?;
|
||||
.wrap_err("updating last_update_date in DB")?;
|
||||
} else {
|
||||
info!("Not sending update for {target}, since not enough time has elapsed since the last one");
|
||||
}
|
||||
|
|
@ -167,13 +166,9 @@ This update is sent after a month of inactivity.
|
|||
.issues()
|
||||
.create_label(target, "d73a4a", format!("Target: {target}"))
|
||||
.await
|
||||
.context("creating label")?;
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err)
|
||||
.context("failed to fetch label label")
|
||||
.map_err(Into::into)
|
||||
.wrap_err("creating label")?;
|
||||
}
|
||||
Err(err) => return Err(err).wrap_err("failed to fetch label label"),
|
||||
}
|
||||
|
||||
let pings = notify_usernames
|
||||
|
|
@ -212,7 +207,7 @@ This issue will be closed automatically when this target works again!"
|
|||
))
|
||||
.send()
|
||||
.await
|
||||
.context("failed to create issue")?;
|
||||
.wrap_err("failed to create issue")?;
|
||||
|
||||
db.insert_notification(NotificationIssue {
|
||||
first_failed_nightly: nightly.into(),
|
||||
|
|
@ -222,7 +217,7 @@ This issue will be closed automatically when this target works again!"
|
|||
last_update_date: Some(jiff::Timestamp::now().as_millisecond()),
|
||||
})
|
||||
.await
|
||||
.context("inserting issue into DB")?;
|
||||
.wrap_err("inserting issue into DB")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -258,7 +253,7 @@ pub async fn notify_build_pass(
|
|||
thanks for playing this round of Tier 3 rustc target breakage fixing! See y'all next time :3!\n\n<{url}>"),
|
||||
)
|
||||
.await
|
||||
.context("creating update comment")?;
|
||||
.wrap_err("creating update comment")?;
|
||||
|
||||
github_client
|
||||
.issues()
|
||||
|
|
@ -267,7 +262,7 @@ pub async fn notify_build_pass(
|
|||
.state_reason(IssueStateReason::Completed)
|
||||
.send()
|
||||
.await
|
||||
.context("closing issue")?;
|
||||
.wrap_err("closing issue")?;
|
||||
|
||||
db.finish_notification(issue.issue_number).await?;
|
||||
}
|
||||
|
|
|
|||
57
src/web.rs
57
src/web.rs
|
|
@ -7,19 +7,15 @@ use axum::{
|
|||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use rootcause::{prelude::ResultExt, Report};
|
||||
use color_eyre::{eyre::Context, Result};
|
||||
use serde::Deserialize;
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::{
|
||||
db::{BuildInfo, BuildMode, BuildStats, Db, Status},
|
||||
notification, Result,
|
||||
notification,
|
||||
};
|
||||
|
||||
mod revs {
|
||||
include!(concat!(env!("OUT_DIR"), "/revs.rs"));
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub db: Db,
|
||||
|
|
@ -32,8 +28,8 @@ pub async fn webserver(db: Db, notification_repo: String) -> Result<()> {
|
|||
.route("/build", get(web_build))
|
||||
.route("/target", get(web_target))
|
||||
.route("/nightly", get(web_nightly))
|
||||
.route(revs::INDEX_CSS_NAME, get(index_css))
|
||||
.route(revs::INDEX_JS_NAME, get(index_js))
|
||||
.route("/index.css", get(index_css))
|
||||
.route("/index.js", get(index_js))
|
||||
.with_state(AppState {
|
||||
db,
|
||||
notification_repo,
|
||||
|
|
@ -42,10 +38,7 @@ pub async fn webserver(db: Db, notification_repo: String) -> Result<()> {
|
|||
info!("Serving website on port 3000 (commit {})", crate::VERSION);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
.context("failed to serve")?;
|
||||
Ok(())
|
||||
axum::serve(listener, app).await.wrap_err("failed to serve")
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -332,39 +325,31 @@ async fn web_root(State(state): State<AppState>) -> impl IntoResponse {
|
|||
Ok(Html(page.render().unwrap()).into_response())
|
||||
}
|
||||
|
||||
render(state).await.unwrap_or_else(|err: Report| {
|
||||
error!(?err, "Error loading data for root page");
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
})
|
||||
}
|
||||
|
||||
fn reply_static(body: &'static str, content_type: &'static str) -> impl IntoResponse {
|
||||
(
|
||||
[
|
||||
(
|
||||
axum::http::header::CONTENT_TYPE,
|
||||
axum::http::HeaderValue::from_static(content_type),
|
||||
),
|
||||
(
|
||||
axum::http::header::CACHE_CONTROL,
|
||||
axum::http::HeaderValue::from_static("public, max-age=31556952, immutable"),
|
||||
),
|
||||
],
|
||||
body,
|
||||
)
|
||||
render(state)
|
||||
.await
|
||||
.unwrap_or_else(|err: color_eyre::eyre::Error| {
|
||||
error!(?err, "Error loading data for root page");
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
})
|
||||
}
|
||||
|
||||
async fn index_css() -> impl IntoResponse {
|
||||
reply_static(
|
||||
(
|
||||
[(
|
||||
axum::http::header::CONTENT_TYPE,
|
||||
axum::http::HeaderValue::from_static("text/css; charset=utf-8"),
|
||||
)],
|
||||
include_str!("../static/index.css"),
|
||||
"text/css; charset=utf-8",
|
||||
)
|
||||
}
|
||||
|
||||
async fn index_js() -> impl IntoResponse {
|
||||
reply_static(
|
||||
(
|
||||
[(
|
||||
axum::http::header::CONTENT_TYPE,
|
||||
axum::http::HeaderValue::from_static("application/javascript; charset=utf-8"),
|
||||
)],
|
||||
include_str!("../static/index.js"),
|
||||
"application/javascript; charset=utf-8",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Build {{nightly}} {{target}}</title>
|
||||
<link rel="stylesheet" href="{{ revs::INDEX_CSS_NAME }}" />
|
||||
<script type="module" defer src="{{ revs::INDEX_JS_NAME }}"></script>
|
||||
<link rel="stylesheet" href="/index.css" />
|
||||
<script type="module" defer src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Does it build?</title>
|
||||
<link rel="stylesheet" href="{{ revs::INDEX_CSS_NAME }}" />
|
||||
<script type="module" defer src="{{ revs::INDEX_JS_NAME }}"></script>
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
<script type="module" defer src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Does it build?</h1>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{nightly}} build history</title>
|
||||
<link rel="stylesheet" href="{{ revs::INDEX_CSS_NAME }}" />
|
||||
<link rel="stylesheet" href="/index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Nightly build state for {{nightly}}</h1>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{target}} build history</title>
|
||||
<link rel="stylesheet" href="{{ revs::INDEX_CSS_NAME }}" />
|
||||
<link rel="stylesheet" href="/index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Target build history for {{target}}</h1>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue