Start of port from web to CLI
This commit is contained in:
parent
11b08785ed
commit
46b28e8bdd
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -134,6 +134,7 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
name = "aocsync"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glog",
|
||||
"log 0.4.17",
|
||||
"rocket 0.5.0-rc.2",
|
||||
|
||||
@ -13,3 +13,4 @@ serde = { version = "1.0.148", features = ["serde_derive"] }
|
||||
rocket_anyhow = "0.1.1"
|
||||
thiserror = "1.0.37"
|
||||
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
|
||||
anyhow = "1.0.66"
|
||||
|
||||
@ -8,8 +8,7 @@ port = 9346
|
||||
# Uncomment to make it production like.
|
||||
#log_level = "critical"
|
||||
|
||||
source_root = "/net/nasx/mnt/storage/aoc/github/"
|
||||
build_root = "/tmp/aocsync/"
|
||||
www_root = "/tmp/aocsync-serve/"
|
||||
|
||||
repos = { akramer = { name = "aoc2022", branch = "main" }, ggriffiniii = { name = "aoc2022", branch = "main" }, wathiede = { name = "advent/2022", branch = "master", first_commit = "bc3feabb4669b7ad8167398a4d37eefe68ea0565" } }
|
||||
repos = { akramer = { url = "https://github.com/akramer/aoc2022", name = "aoc2022", branch = "main" }, ggriffiniii = { url = "https://github.com/ggriffiniii/aoc2022", name = "aoc2022", branch = "main" }, wathiede = { url = "https://github.com/wathiede/advent", name = "advent/2022", branch = "master", first_commit = "bc3feabb4669b7ad8167398a4d37eefe68ea0565", subdir = "2022" } }
|
||||
|
||||
34
src/bin/aocpoller.rs
Normal file
34
src/bin/aocpoller.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use std::process::Command;
|
||||
|
||||
use aocsync::{logging_run, Config};
|
||||
use glog::Flags;
|
||||
use log::info;
|
||||
fn main() -> anyhow::Result<()> {
|
||||
glog::new()
|
||||
.init(Flags {
|
||||
colorlogtostderr: true,
|
||||
//alsologtostderr: true, // use logtostderr to only write to stderr and not to files
|
||||
logtostderr: true,
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
let config = rocket::Config::figment()
|
||||
.extract::<Config>()
|
||||
.expect("Couldn't parse config");
|
||||
info!("{:#?}", config);
|
||||
for (name, repo) in config.repos.iter() {
|
||||
let ts = logging_run(
|
||||
Command::new("git")
|
||||
.arg("ls-remote")
|
||||
.arg(&repo.url)
|
||||
.arg("-q")
|
||||
.arg(&repo.name)
|
||||
.arg("-h")
|
||||
.arg(&repo.branch),
|
||||
)?;
|
||||
let out = String::from_utf8_lossy(&ts.output.stdout);
|
||||
let head = &out[..out.find('\t').expect("No tab in output")];
|
||||
info!("HEAD: '{}'", head);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
93
src/lib.rs
Normal file
93
src/lib.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Output},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use log::info;
|
||||
use rocket::response::Responder;
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, Responder)]
|
||||
pub enum SyncError {
|
||||
#[error("IO error")]
|
||||
IoError(#[from] io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
pub build_root: PathBuf,
|
||||
pub www_root: PathBuf,
|
||||
pub repos: HashMap<String, Repo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Repo {
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
pub branch: String,
|
||||
pub first_commit: Option<String>,
|
||||
pub subdir: Option<String>,
|
||||
}
|
||||
|
||||
pub fn indent_output(bytes: &[u8]) -> String {
|
||||
let s = String::from_utf8_lossy(bytes);
|
||||
let s = if s.is_empty() { "<NO OUTPUT>" } else { &s };
|
||||
s.split('\n')
|
||||
.map(|l| format!("\t{l}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
pub 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)
|
||||
)
|
||||
}
|
||||
|
||||
pub struct TaskStatus {
|
||||
pub command: String,
|
||||
pub output: Output,
|
||||
pub duration: Duration,
|
||||
}
|
||||
|
||||
pub fn logging_run(cmd: &mut Command) -> Result<TaskStatus, SyncError> {
|
||||
info!(
|
||||
"{}$ {} {}",
|
||||
cmd.get_current_dir()
|
||||
.unwrap_or(Path::new("<NO CWD>"))
|
||||
.display(),
|
||||
cmd.get_program().to_string_lossy(),
|
||||
cmd.get_args()
|
||||
.map(|a| a.to_string_lossy())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" "),
|
||||
);
|
||||
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)
|
||||
}
|
||||
|
||||
pub 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),
|
||||
)
|
||||
}
|
||||
137
src/main.rs
137
src/main.rs
@ -7,90 +7,32 @@ use std::{
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Output},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use aocsync::{format_task_status, git_checkout, logging_run, Config, SyncError, TaskStatus};
|
||||
use glog::Flags;
|
||||
use log::{error, info};
|
||||
use rocket::{fairing::AdHoc, fs::FileServer, response::Responder, State};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, Responder)]
|
||||
pub enum SyncError {
|
||||
#[error("IO error")]
|
||||
IoError(#[from] io::Error),
|
||||
}
|
||||
|
||||
// For testing
|
||||
#[get("/-/reload/<repo>")]
|
||||
fn get_reload(config: &State<Config>, repo: &str) -> Result<String, SyncError> {
|
||||
reload(config, repo)
|
||||
fn get_reload(
|
||||
lock: &State<ReloadLock>,
|
||||
config: &State<Config>,
|
||||
repo: &str,
|
||||
) -> Result<String, SyncError> {
|
||||
reload(lock, config, repo)
|
||||
}
|
||||
|
||||
#[post("/-/reload/<repo>")]
|
||||
fn post_reload(config: &State<Config>, repo: &str) -> Result<String, SyncError> {
|
||||
reload(config, repo)
|
||||
}
|
||||
|
||||
fn indent_output(bytes: &[u8]) -> String {
|
||||
let s = String::from_utf8_lossy(bytes);
|
||||
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> {
|
||||
info!(
|
||||
"{}$ {} {}",
|
||||
cmd.get_current_dir()
|
||||
.unwrap_or(Path::new("<NO CWD>"))
|
||||
.display(),
|
||||
cmd.get_program().to_string_lossy(),
|
||||
cmd.get_args()
|
||||
.map(|a| a.to_string_lossy())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" "),
|
||||
);
|
||||
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 post_reload(
|
||||
lock: &State<ReloadLock>,
|
||||
config: &State<Config>,
|
||||
repo: &str,
|
||||
) -> Result<String, SyncError> {
|
||||
reload(lock, config, repo)
|
||||
}
|
||||
|
||||
fn bench_at_commit(
|
||||
@ -113,7 +55,14 @@ fn bench_at_commit(
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn reload(config: &State<Config>, name: &str) -> Result<String, SyncError> {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
fn reload(
|
||||
lock: &State<ReloadLock>,
|
||||
config: &State<Config>,
|
||||
name: &str,
|
||||
) -> Result<String, SyncError> {
|
||||
let _locked = lock.0.lock().unwrap();
|
||||
let repo = &config.repos[name];
|
||||
info!("Need to reload '{}': {:?}\n{:#?}", name, config, repo);
|
||||
|
||||
@ -122,12 +71,15 @@ fn reload(config: &State<Config>, name: &str) -> Result<String, SyncError> {
|
||||
let www_root = config.www_root.join(name);
|
||||
let target_root = config.build_root.join("target");
|
||||
|
||||
let source_path = config.source_root.join(name).join(&repo.name);
|
||||
let build_path = git_root.join(&repo.name);
|
||||
let checkout_path = git_root.join(&repo.name);
|
||||
let build_path = if let Some(subdir) = &repo.subdir {
|
||||
git_root.join(&repo.name).join(subdir)
|
||||
} else {
|
||||
git_root.join(&repo.name)
|
||||
};
|
||||
let target_path = target_root.join(name);
|
||||
|
||||
dbg!(
|
||||
&source_path,
|
||||
&build_path,
|
||||
&target_path,
|
||||
&commits_root,
|
||||
@ -152,28 +104,35 @@ fn reload(config: &State<Config>, name: &str) -> Result<String, SyncError> {
|
||||
fs::create_dir_all(&www_root)?;
|
||||
}
|
||||
let mut output = vec![logging_run(Command::new("git").arg("version"))?];
|
||||
let needs_clone = !build_path.exists();
|
||||
let needs_clone = !checkout_path.exists();
|
||||
if needs_clone {
|
||||
output.push(logging_run(
|
||||
Command::new("git")
|
||||
.current_dir(&git_root)
|
||||
.arg("clone")
|
||||
.arg(&source_path),
|
||||
.arg(&repo.url)
|
||||
.arg(&checkout_path),
|
||||
)?);
|
||||
}
|
||||
output.push(logging_run(
|
||||
Command::new("git")
|
||||
.current_dir(&build_path)
|
||||
.current_dir(&checkout_path)
|
||||
.arg("checkout")
|
||||
.arg("-f")
|
||||
.arg(&repo.branch),
|
||||
)?);
|
||||
dbg!(&build_path);
|
||||
output.push(logging_run(
|
||||
Command::new("git")
|
||||
.current_dir(&checkout_path)
|
||||
.arg("checkout")
|
||||
.arg("HEAD"),
|
||||
)?);
|
||||
dbg!(&checkout_path);
|
||||
// Make sure buildable clone is up to date
|
||||
output.push(logging_run(
|
||||
Command::new("git").current_dir(&build_path).arg("fetch"),
|
||||
Command::new("git").current_dir(&checkout_path).arg("pull"),
|
||||
)?);
|
||||
let commits = logging_run(Command::new("git").current_dir(&build_path).args([
|
||||
let commits = logging_run(Command::new("git").current_dir(&checkout_path).args([
|
||||
"log",
|
||||
"--format=%H",
|
||||
&repo.branch,
|
||||
@ -243,20 +202,7 @@ fn http500(req: &rocket::Request) -> String {
|
||||
format!("{:?}", req)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Config {
|
||||
source_root: PathBuf,
|
||||
build_root: PathBuf,
|
||||
www_root: PathBuf,
|
||||
repos: HashMap<String, Repo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Repo {
|
||||
name: String,
|
||||
branch: String,
|
||||
first_commit: Option<String>,
|
||||
}
|
||||
struct ReloadLock(Arc<Mutex<()>>);
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
@ -276,6 +222,7 @@ fn rocket() -> _ {
|
||||
rocket::build()
|
||||
.mount("/", routes![index, get_reload, post_reload])
|
||||
.mount("/results/", FileServer::from(config.www_root))
|
||||
.manage(ReloadLock(Arc::new(Mutex::new(()))))
|
||||
//.register("/", catchers![http500])
|
||||
.attach(AdHoc::config::<Config>())
|
||||
.attach(Template::fairing())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user