Run bench for each commit.

This commit is contained in:
Bill Thiede 2022-12-03 20:31:19 -08:00
parent 0ee02d199e
commit c67fa96700

View File

@ -1,9 +1,12 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use std::{ use std::{
fs, io, fs,
fs::File,
io,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::{Command, Output},
time::{Duration, Instant},
}; };
use glog::Flags; use glog::Flags;
@ -29,63 +32,149 @@ fn post_reload(config: &State<Config>, repo: &str) -> Result<String, SyncError>
reload(config, repo) reload(config, repo)
} }
fn logging_run(cmd: &mut Command) -> &mut Command { fn indent_output(bytes: &[u8]) -> String {
info!("Running {cmd:?}"); let s = String::from_utf8_lossy(bytes);
cmd let s = if s.is_empty() { "<NO OUTPUT>" } else { &s };
s.split('\n')
.map(|l| format!("\t{l}"))
.collect::<Vec<_>>()
.join("\n")
}
fn format_task_status(ts: &TaskStatus) -> String {
format!(
"Status {} ({}) {}s:\nStdout:\n{}\nStderr:\n{}",
ts.command,
ts.output.status,
ts.duration.as_secs_f32(),
indent_output(&ts.output.stdout),
indent_output(&ts.output.stderr)
)
}
struct TaskStatus {
command: String,
output: Output,
duration: Duration,
}
fn logging_run(cmd: &mut Command) -> Result<TaskStatus, SyncError> {
let start = Instant::now();
let o = cmd.output()?;
let ts = TaskStatus {
output: o,
command: format!("{:?}", cmd),
duration: start.elapsed(),
};
info!("{}", format_task_status(&ts));
Ok(ts)
}
fn git_checkout(build_path: &Path, rev: &str) -> Result<TaskStatus, SyncError> {
logging_run(
Command::new("git")
.current_dir(&build_path)
.arg("checkout")
.arg("-q")
.arg(rev),
)
}
fn bench_at_commit(
commit: &str,
build_path: &Path,
target_path: &Path,
) -> Result<Vec<TaskStatus>, SyncError> {
let mut output = Vec::new();
output.push(git_checkout(build_path, commit)?);
// Run `cargo aoc bench`
output.push(logging_run(
Command::new("cargo")
.env("CARGO_TARGET_DIR", target_path)
.current_dir(&build_path)
.arg("aoc")
.arg("bench"),
)?);
output.push(git_checkout(build_path, "HEAD")?);
Ok(output)
} }
fn reload(config: &State<Config>, repo: &str) -> Result<String, SyncError> { fn reload(config: &State<Config>, repo: &str) -> Result<String, SyncError> {
info!("Need to reload '{}': {:?}", repo, config); info!("Need to reload '{}': {:?}", repo, config);
let source_path = config.source_root.join(repo); let source_path = config.source_root.join(repo);
let build_path = config.build_root.join(repo); let build_path = config.build_root.join("git").join(repo);
dbg!(&build_path); let target_path = config.build_root.join("target").join(repo);
dbg!(&source_path); let commits_root = config.build_root.join("commits");
let www_root = config.www_root.join(repo);
dbg!(
&source_path,
&build_path,
&target_path,
&commits_root,
&www_root
);
if !commits_root.exists() {
info!("Creating {}", commits_root.display());
fs::create_dir_all(&commits_root)?;
}
if !www_root.exists() {
info!("Creating {}", www_root.display());
fs::create_dir_all(&www_root)?;
}
let mut output = Vec::new(); let mut output = Vec::new();
let needs_clone = !build_path.exists(); let needs_clone = !build_path.exists();
if needs_clone { if needs_clone {
output.push( output.push(logging_run(
logging_run( Command::new("git")
Command::new("/run/current-system/sw/bin/git")
.current_dir(&config.build_root) .current_dir(&config.build_root)
.arg("clone") .arg("clone")
.arg(&source_path) .arg(&source_path)
.arg(&build_path), .arg(&build_path),
) )?);
.output()?,
);
} }
output.push(logging_run(
Command::new("git")
.current_dir(&config.build_root)
.arg("checkout")
.arg("-f")
.arg("origin"),
)?);
// Make sure buildable clone is up to date // Make sure buildable clone is up to date
output.push( output.push(logging_run(
logging_run( Command::new("git").current_dir(&build_path).arg("pull"),
Command::new("/run/current-system/sw/bin/git") )?);
.current_dir(&build_path) let commits = logging_run(Command::new("git").current_dir(&build_path).args([
.arg("pull"), "log",
) "--format=%H",
.output()?, "origin",
); ]))?;
// Run `cargo aoc bench` let binding = String::from_utf8_lossy(&commits.output.stdout).into_owned();
output.push( let mut unknown_commits: Vec<_> = binding
logging_run( .lines()
Command::new("cargo") .filter(|commit| !commits_root.join(commit).exists())
.current_dir(&build_path) .collect();
.arg("aoc") unknown_commits.reverse();
.arg("bench"), output.push(commits);
) info!("Need to bench commits: {:?}", unknown_commits);
.output()?, for commit in unknown_commits {
); match bench_at_commit(commit, &build_path, &target_path) {
Ok(outputs) => {
output.extend(outputs);
File::create(commits_root.join(commit))?;
}
Err(err) => error!("Failed to bench {}@{}: {}", repo, commit, err),
}
}
// Copy files from `target/` to serving directory // Copy files from `target/` to serving directory
let bench_path = build_path.join("target/aoc/aoc-autobench/target/criterion"); let bench_path = target_path.join("criterion");
copy_dir_all(bench_path, config.www_root.join(repo))?; info!("Copying {} -> {}", bench_path.display(), www_root.display());
copy_dir_all(bench_path, www_root)?;
let response = output let response = output
.iter() .iter()
.map(|o| { .map(|ts| format!("{}", format_task_status(ts)))
format!(
"Status {}:\nStdout: {}\nStderr: {}",
o.status,
String::from_utf8_lossy(&o.stdout),
String::from_utf8_lossy(&o.stderr)
)
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"); .join("\n");
info!("{}", response); info!("{}", response);
@ -138,6 +227,6 @@ fn rocket() -> _ {
.unwrap(); .unwrap();
rocket::build() rocket::build()
.mount("/", routes![index, get_reload, post_reload]) .mount("/", routes![index, get_reload, post_reload])
.register("/", catchers![http500]) //.register("/", catchers![http500])
.attach(AdHoc::config::<Config>()) .attach(AdHoc::config::<Config>())
} }