diff --git a/src/main.rs b/src/main.rs index b244e83..561d3ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,9 +10,10 @@ extern crate staticfile; // point your browser to http://127.0.0.1:3000/doc/ use std::path::{Path, PathBuf}; +use std::error::Error; -use iron::{Iron, IronResult, status}; use iron::prelude::{Chain, Request, Response}; +use iron::{status, Iron, IronResult}; use mount::Mount; use staticfile::Static; @@ -24,51 +25,120 @@ struct WebHookConfig { repo_prefixes: Vec, } -fn redeploy_repo(url: &str, path: &Path) -> Result<(), ()> { - eprintln!("Cloning {}", url); - let repo = match Repository::clone(url, path) { +fn redeploy_repo(url: &str, path: &Path, name: &str) -> Result<(), git2::Error> { + let mut new_path = path.to_path_buf(); + new_path.push(name); + eprintln!("Cloning {} into {:?} ({:?} / {})", url, new_path, path, name); + + let repo = match Repository::open(&new_path) { Ok(repo) => repo, - Err(e) => panic!("failed to clone: {}", e), + Err(e) => { + eprintln!("can't open repo, going to try cloning it: {}", e.description()); + match Repository::clone(url, &new_path) { + Ok(repo) => repo, + Err(e) => panic!("failed to clone: {}", e), + } + } }; + repo.set_head("FETCH_HEAD")?; + + // repo.checkout_tree(&branch.into_reference(), Some(git2::build::CheckoutBuilder::new().force().use_theirs(true)))?; + repo.checkout_head(Some(git2::build::CheckoutBuilder::new().force().use_theirs(true)))?; Ok(()) } fn webhook(req: &mut Request, config: &WebHookConfig) -> IronResult { eprintln!("Webhook Request: {:?}", req); - let v: serde_json::Value = serde_json::from_reader(&mut req.body).unwrap(); + let wh: serde_json::Value = serde_json::from_reader(&mut req.body).unwrap(); - if let Some(found_secret) = v.get("secret") { - if found_secret != &config.secret { - return Ok(Response::with(status::Unauthorized)); - } + // Ensure we have a "Secret" parameter, and that it matches the config. + let found_secret = wh.get("secret"); + if found_secret.is_none() { + return Ok(Response::with(status::Unauthorized)); } - else { + let found_secret = found_secret.unwrap(); + if found_secret != &config.secret { return Ok(Response::with(status::Unauthorized)); } - if let Some(repository) = v.get("repository") { - if let Some(html_url) = repository.get("html_url") { - if let Some(html_url_str) = html_url.as_str() { - if html_url_str.starts_with("https://git.xobs.io/xobs") { - redeploy_repo(html_url_str, &Path::new("C:\\Code\\talkserved")); - } - else { - eprintln!("HTML URL isn't a string"); - } - } - else { - eprintln!("URL doesn't start with match"); - } - } - else { + // Ensure the repo is valid. + let repository = wh.get("repository"); + if repository.is_none() { + eprintln!("No 'repository' key found"); + return Ok(Response::with(status::PreconditionRequired)); + } + let repository = repository.unwrap(); + + // Ensure there's an HTML URL that's a valid string + let html_url = match repository.get("html_url") { + None => { eprintln!("No HTML URL found"); + return Ok(Response::with(status::PreconditionRequired)); + } + Some(s) => match s.as_str() { + None => { + eprintln!("HTML URL isn't a string"); + return Ok(Response::with(status::PreconditionRequired)); + } + Some(s) => s, + }, + }; + + // Check to make sure the repo prefix is one we recognize + let mut found_match = false; + for prefix in &config.repo_prefixes { + if html_url.starts_with(prefix) { + found_match = true; + break; } } - else { - eprintln!("No repository key found"); + if !found_match { + eprintln!("URL doesn't start with match"); + return Ok(Response::with(status::PreconditionRequired)); } - // eprintln!("Value: {:?}", v); + + // And ensure the URL is a string + let website_url = match repository.get("website") { + None => { + eprintln!("The website isn't configured for this repo"); + return Ok(Response::with(status::PreconditionRequired)); + } + Some(s) => match s.as_str() { + None => { + eprintln!("Website URL isn't a string"); + return Ok(Response::with(status::PreconditionRequired)); + } + Some(s) => match iron::Url::parse(s) { + Ok(u) => u, + Err(e) => { + eprintln!("Unable to parse URL: {:?}", e); + return Ok(Response::with(status::PreconditionFailed)); + } + }, + }, + }; + let target_path = match website_url.path().last() { + Some(s) => { + if s.is_empty() { + eprintln!("No website URL path was found -- defaulting to \"current\""); + "current".to_owned() + } else { + s.to_string() + } + } + None => { + eprintln!("No website URL path was found"); + return Ok(Response::with(status::PreconditionFailed)); + } + }; + eprintln!("Final path: {:?} / {:?}", config.repo_root, target_path); + + if let Err(e) = redeploy_repo(html_url, &config.repo_root, &target_path) { + eprintln!("unable to clone/update repo: {}", e.description()); + return Ok(Response::with(status::NotModified)); + } + Ok(Response::with(status::Ok)) } @@ -85,9 +155,10 @@ fn main() { mount.mount("/", Static::new(config.repo_root.clone())); // Listen for calls to "/webhook" and process accordingly - mount.mount("/webhook", Chain::new(move |req: &mut Request| { - webhook(req, &config) - })); + mount.mount( + "/webhook", + Chain::new(move |req: &mut Request| webhook(req, &config)), + ); println!("Doc server running on http://{}", hostaddr);