feat: handle Git errors

This commit is contained in:
Aravinth Manivannan 2022-04-26 20:12:17 +05:30
parent eb87453bd1
commit b758042836
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
3 changed files with 61 additions and 32 deletions

View File

@ -25,6 +25,7 @@ use actix_web::{
}; };
use config::ConfigError as ConfigErrorInner; use config::ConfigError as ConfigErrorInner;
use derive_more::{Display, Error}; use derive_more::{Display, Error};
use git2::Error as GitError;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::ParseError; use url::ParseError;
@ -118,6 +119,9 @@ pub enum ServiceError {
#[display(fmt = "Configuration Error {}", _0)] #[display(fmt = "Configuration Error {}", _0)]
ConfigError(ConfigError), ConfigError(ConfigError),
#[display(fmt = "Git Error {}", _0)]
GitError(GitError),
} }
impl From<ParseError> for ServiceError { impl From<ParseError> for ServiceError {
@ -127,6 +131,13 @@ impl From<ParseError> for ServiceError {
} }
} }
impl From<GitError> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(e: GitError) -> ServiceError {
ServiceError::GitError(e)
}
}
/// Generic result data structure /// Generic result data structure
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
pub type ServiceResult<V> = std::result::Result<V, ServiceError>; pub type ServiceResult<V> = std::result::Result<V, ServiceError>;
@ -167,6 +178,7 @@ impl ResponseError for ServiceError {
ServiceError::UnauthorizedOperation(_) => StatusCode::UNAUTHORIZED, ServiceError::UnauthorizedOperation(_) => StatusCode::UNAUTHORIZED,
ServiceError::BadRequest(_) => StatusCode::BAD_REQUEST, ServiceError::BadRequest(_) => StatusCode::BAD_REQUEST,
ServiceError::GitError(_) => StatusCode::BAD_REQUEST,
} }
} }
} }

View File

@ -18,6 +18,8 @@ use git2::{build::CheckoutBuilder, BranchType, Direction, ObjectType, Repository
use log::info; use log::info;
use serde::Deserialize; use serde::Deserialize;
use crate::errors::*;
#[derive(Debug, Clone, PartialEq, Deserialize)] #[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct Page { pub struct Page {
pub secret: String, pub secret: String,
@ -27,50 +29,50 @@ pub struct Page {
} }
impl Page { impl Page {
fn create_repo(&self) -> Repository { fn create_repo(&self) -> ServiceResult<Repository> {
let repo = Repository::open(&self.path); let repo = Repository::open(&self.path);
if let Ok(repo) = repo { if let Ok(repo) = repo {
return repo; return Ok(repo);
} else { } else {
info!("Cloning repository {} at {}", self.repo, self.path); info!("Cloning repository {} at {}", self.repo, self.path);
Repository::clone(&self.repo, &self.path).unwrap() Repository::clone(&self.repo, &self.path)?;
}; };
let repo = Repository::open(&self.path).unwrap(); let repo = Repository::open(&self.path)?;
self._fetch_upstream(&repo, &self.branch); self._fetch_upstream(&repo, &self.branch)?;
self.deploy_branch(&repo); self.deploy_branch(&repo)?;
repo Ok(repo)
} }
pub fn deploy_branch(&self, repo: &Repository) { pub fn deploy_branch(&self, repo: &Repository) -> ServiceResult<()> {
let branch = repo let branch = repo.find_branch(&format!("origin/{}", &self.branch), BranchType::Remote)?;
.find_branch(&format!("origin/{}", &self.branch), BranchType::Remote)
.unwrap();
let mut checkout_options = CheckoutBuilder::new(); let mut checkout_options = CheckoutBuilder::new();
checkout_options.force(); checkout_options.force();
let tree = branch.get().peel(ObjectType::Tree).unwrap(); let tree = branch.get().peel(ObjectType::Tree)?;
repo.checkout_tree(&tree, Some(&mut checkout_options)) repo.checkout_tree(&tree, Some(&mut checkout_options))?;
.unwrap(); repo.set_head(branch.get().name().unwrap())?;
repo.set_head(branch.get().name().unwrap()).unwrap();
info!("Deploying branch {}", self.branch); info!("Deploying branch {}", self.branch);
Ok(())
} }
fn _fetch_upstream(&self, repo: &Repository, branch: &str) { fn _fetch_upstream(&self, repo: &Repository, branch: &str) -> ServiceResult<()> {
let mut remote = repo.find_remote("origin").unwrap(); let mut remote = repo.find_remote("origin").unwrap();
remote.connect(Direction::Fetch).unwrap(); remote.connect(Direction::Fetch)?;
info!("Updating repository {}", self.repo); info!("Updating repository {}", self.repo);
remote.fetch(&[branch], None, None).unwrap(); remote.fetch(&[branch], None, None)?;
remote.disconnect().unwrap(); remote.disconnect()?;
Ok(())
} }
pub fn update(&self) { pub fn update(&self) -> ServiceResult<()> {
let repo = self.create_repo(); let repo = self.create_repo()?;
self._fetch_upstream(&repo, &self.branch); self._fetch_upstream(&repo, &self.branch)?;
self.deploy_branch(&repo); self.deploy_branch(&repo)?;
Ok(())
} }
} }
@ -105,9 +107,9 @@ mod tests {
"repository doesn't exist yet" "repository doesn't exist yet"
); );
let repo = page.create_repo(); let repo = page.create_repo().unwrap();
assert!(!repo.is_bare(), "repository isn't bare"); assert!(!repo.is_bare(), "repository isn't bare");
page.create_repo(); page.create_repo().unwrap();
assert!( assert!(
Repository::open(tmp_dir.as_path()).is_ok(), Repository::open(tmp_dir.as_path()).is_ok(),
"repository exists yet" "repository exists yet"
@ -119,7 +121,7 @@ mod tests {
&"origin/gh-pages" &"origin/gh-pages"
); );
page.branch = "master".to_string(); page.branch = "master".to_string();
page.update(); page.update().unwrap();
let master = page.get_tree(&repo); let master = page.get_tree(&repo);
assert_eq!(master.name().unwrap().as_ref().unwrap(), &"origin/master"); assert_eq!(master.name().unwrap().as_ref().unwrap(), &"origin/master");
} }

View File

@ -17,11 +17,12 @@
use std::env; use std::env;
use std::path::Path; use std::path::Path;
use config::{Config, ConfigError, Environment, File}; use config::{Config, Environment, File};
use log::warn; use log::warn;
use serde::Deserialize; use serde::Deserialize;
use url::Url; use url::Url;
use crate::errors::*;
use crate::page::Page; use crate::page::Page;
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@ -47,7 +48,7 @@ pub struct Settings {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
impl Settings { impl Settings {
pub fn new() -> Result<Self, ConfigError> { pub fn new() -> ServiceResult<Self> {
let mut s = Config::builder(); let mut s = Config::builder();
const CURRENT_DIR: &str = "./config/default.toml"; const CURRENT_DIR: &str = "./config/default.toml";
@ -99,12 +100,26 @@ impl Settings {
if index2 == index { if index2 == index {
continue; continue;
} }
if page.secret == page2.secret || page.repo == page2.repo || page.path == page2.path if page.secret == page2.secret {
{ log::error!(
panic!("duplicate page onfiguration {:?} and {:?}", page, page2); "{}",
ServiceError::SecretTaken(page.to_owned(), page2.to_owned())
);
} else if page.repo == page2.repo {
log::error!(
"{}",
ServiceError::DuplicateRepositoryURL(page.to_owned(), page2.to_owned(),)
);
} else if page.path == page2.path {
log::error!(
"{}",
ServiceError::PathTaken(page.to_owned(), page2.to_owned())
);
} }
} }
page.update(); if let Err(e) = page.update() {
log::error!("{e}");
}
} }
Ok(settings) Ok(settings)