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"
|
name = "aocsync"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"glog",
|
"glog",
|
||||||
"log 0.4.17",
|
"log 0.4.17",
|
||||||
"rocket 0.5.0-rc.2",
|
"rocket 0.5.0-rc.2",
|
||||||
|
|||||||
@ -13,3 +13,4 @@ serde = { version = "1.0.148", features = ["serde_derive"] }
|
|||||||
rocket_anyhow = "0.1.1"
|
rocket_anyhow = "0.1.1"
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
|
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.
|
# Uncomment to make it production like.
|
||||||
#log_level = "critical"
|
#log_level = "critical"
|
||||||
|
|
||||||
source_root = "/net/nasx/mnt/storage/aoc/github/"
|
|
||||||
build_root = "/tmp/aocsync/"
|
build_root = "/tmp/aocsync/"
|
||||||
www_root = "/tmp/aocsync-serve/"
|
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,
|
io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Command, Output},
|
process::{Command, Output},
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use aocsync::{format_task_status, git_checkout, logging_run, Config, SyncError, TaskStatus};
|
||||||
use glog::Flags;
|
use glog::Flags;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use rocket::{fairing::AdHoc, fs::FileServer, response::Responder, State};
|
use rocket::{fairing::AdHoc, fs::FileServer, response::Responder, State};
|
||||||
use rocket_dyn_templates::{context, Template};
|
use rocket_dyn_templates::{context, Template};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Error, Debug, Responder)]
|
|
||||||
pub enum SyncError {
|
|
||||||
#[error("IO error")]
|
|
||||||
IoError(#[from] io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
// For testing
|
// For testing
|
||||||
#[get("/-/reload/<repo>")]
|
#[get("/-/reload/<repo>")]
|
||||||
fn get_reload(config: &State<Config>, repo: &str) -> Result<String, SyncError> {
|
fn get_reload(
|
||||||
reload(config, repo)
|
lock: &State<ReloadLock>,
|
||||||
|
config: &State<Config>,
|
||||||
|
repo: &str,
|
||||||
|
) -> Result<String, SyncError> {
|
||||||
|
reload(lock, config, repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/-/reload/<repo>")]
|
#[post("/-/reload/<repo>")]
|
||||||
fn post_reload(config: &State<Config>, repo: &str) -> Result<String, SyncError> {
|
fn post_reload(
|
||||||
reload(config, repo)
|
lock: &State<ReloadLock>,
|
||||||
}
|
config: &State<Config>,
|
||||||
|
repo: &str,
|
||||||
fn indent_output(bytes: &[u8]) -> String {
|
) -> Result<String, SyncError> {
|
||||||
let s = String::from_utf8_lossy(bytes);
|
reload(lock, config, repo)
|
||||||
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 bench_at_commit(
|
fn bench_at_commit(
|
||||||
@ -113,7 +55,14 @@ fn bench_at_commit(
|
|||||||
Ok(output)
|
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];
|
let repo = &config.repos[name];
|
||||||
info!("Need to reload '{}': {:?}\n{:#?}", name, config, repo);
|
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 www_root = config.www_root.join(name);
|
||||||
let target_root = config.build_root.join("target");
|
let target_root = config.build_root.join("target");
|
||||||
|
|
||||||
let source_path = config.source_root.join(name).join(&repo.name);
|
let checkout_path = git_root.join(&repo.name);
|
||||||
let build_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);
|
let target_path = target_root.join(name);
|
||||||
|
|
||||||
dbg!(
|
dbg!(
|
||||||
&source_path,
|
|
||||||
&build_path,
|
&build_path,
|
||||||
&target_path,
|
&target_path,
|
||||||
&commits_root,
|
&commits_root,
|
||||||
@ -152,28 +104,35 @@ fn reload(config: &State<Config>, name: &str) -> Result<String, SyncError> {
|
|||||||
fs::create_dir_all(&www_root)?;
|
fs::create_dir_all(&www_root)?;
|
||||||
}
|
}
|
||||||
let mut output = vec![logging_run(Command::new("git").arg("version"))?];
|
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 {
|
if needs_clone {
|
||||||
output.push(logging_run(
|
output.push(logging_run(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(&git_root)
|
.current_dir(&git_root)
|
||||||
.arg("clone")
|
.arg("clone")
|
||||||
.arg(&source_path),
|
.arg(&repo.url)
|
||||||
|
.arg(&checkout_path),
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
output.push(logging_run(
|
output.push(logging_run(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(&build_path)
|
.current_dir(&checkout_path)
|
||||||
.arg("checkout")
|
.arg("checkout")
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
.arg(&repo.branch),
|
.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
|
// Make sure buildable clone is up to date
|
||||||
output.push(logging_run(
|
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",
|
"log",
|
||||||
"--format=%H",
|
"--format=%H",
|
||||||
&repo.branch,
|
&repo.branch,
|
||||||
@ -243,20 +202,7 @@ fn http500(req: &rocket::Request) -> String {
|
|||||||
format!("{:?}", req)
|
format!("{:?}", req)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
struct ReloadLock(Arc<Mutex<()>>);
|
||||||
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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> _ {
|
fn rocket() -> _ {
|
||||||
@ -276,6 +222,7 @@ fn rocket() -> _ {
|
|||||||
rocket::build()
|
rocket::build()
|
||||||
.mount("/", routes![index, get_reload, post_reload])
|
.mount("/", routes![index, get_reload, post_reload])
|
||||||
.mount("/results/", FileServer::from(config.www_root))
|
.mount("/results/", FileServer::from(config.www_root))
|
||||||
|
.manage(ReloadLock(Arc::new(Mutex::new(()))))
|
||||||
//.register("/", catchers![http500])
|
//.register("/", catchers![http500])
|
||||||
.attach(AdHoc::config::<Config>())
|
.attach(AdHoc::config::<Config>())
|
||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user