cinderella

[unmaintained] simple CI engine
Log | Files | Refs | README | LICENSE

commit 68b85da189c1fa158329deb89b1b9110667f98b5
parent d7e35c1bf058b3cebb5231154b7e704bf53ebc6d
Author: Stefan Koch <programming@stefan-koch.name>
Date:   Fri,  1 May 2020 10:13:21 +0200

allow cinderella to write build badges to a folder

Diffstat:
Aassets/icon_build_error.png | 0
Aassets/icon_build_success.png | 0
Msrc/config.rs | 13+++++++++++++
Asrc/dashboard.rs | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib.rs | 21+++++++++++++++++++++
5 files changed, 109 insertions(+), 0 deletions(-)

diff --git a/assets/icon_build_error.png b/assets/icon_build_error.png Binary files differ. diff --git a/assets/icon_build_success.png b/assets/icon_build_success.png Binary files differ. diff --git a/src/config.rs b/src/config.rs @@ -13,6 +13,7 @@ pub struct Configs<'a> { pub struct CinderellaConfig { pub email: Option<Email>, pub secrets: Option<Secrets>, + pub dashboard: Option<Dashboard>, } #[derive(Deserialize, Debug)] @@ -29,6 +30,11 @@ pub struct Secrets { pub password: String, } +#[derive(Deserialize, Debug)] +pub struct Dashboard { + pub folder: String, +} + impl CinderellaConfig { pub fn from_file(path: PathBuf) -> CinderellaConfig { match fs::read_to_string(path) { @@ -38,6 +44,7 @@ impl CinderellaConfig { _ => CinderellaConfig { email: None, secrets: None, + dashboard: None, } } } @@ -92,6 +99,9 @@ mod tests { password = "s" to = "to@example.com" from = "from@example.com" + + [dashboard] + folder = "/var/www/cinderella" "#; let mut tmpfile = NamedTempFile::new().unwrap(); let f = tmpfile.as_file_mut(); @@ -105,6 +115,9 @@ mod tests { assert_eq!(email.password, "s"); assert_eq!(email.to, "to@example.com"); assert_eq!(email.from, "from@example.com"); + + let dashboard = config.dashboard.unwrap(); + assert_eq!(dashboard.folder, "/var/www/cinderella"); } #[test] diff --git a/src/dashboard.rs b/src/dashboard.rs @@ -0,0 +1,74 @@ +use std::path::PathBuf; +use std::io::prelude::Write; +use std::fs::{self, File}; + +pub enum BuildStatus { + Success, + Error(String), +} + +static ICON_SUCCESS: &'static [u8] = include_bytes!("../assets/icon_build_success.png"); +static ICON_ERROR: &'static [u8] = include_bytes!("../assets/icon_build_error.png"); + +pub fn generate_status_icon(project: &str, branch: &str, status: BuildStatus, dir: &PathBuf) { + let mut path = dir.clone(); + path.push(project); + + // ignore the AlreadyExists error + // other errors are also not important, because + // we recognize them once the file cannot be written + let _ = fs::create_dir(path.clone()); + + path.push(format!("{}.png", branch)); + + // TODO: Error handling + let mut buffer = File::create(path).unwrap(); + match status { + BuildStatus::Success => buffer.write(ICON_SUCCESS).expect("Could not write to file"), + BuildStatus::Error(_) => buffer.write(ICON_ERROR).expect("Could not write to file"), + }; +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::prelude::Read; + + #[test] + fn generate_success_status_icon() { + let dir = tempfile::tempdir().unwrap(); + let pathbuf = dir.path().to_path_buf(); + + generate_status_icon("myproject", "master", BuildStatus::Success, &pathbuf); + + let mut buf = dir.path().to_path_buf(); + buf.push("myproject"); + buf.push("master.png"); + assert!(buf.exists()); + + let mut f = File::open(buf).unwrap(); + let mut buffer = Vec::new(); + f.read_to_end(&mut buffer).unwrap(); + + assert_eq!(buffer, ICON_SUCCESS); + } + + #[test] + fn generate_error_status_icon() { + let dir = tempfile::tempdir().unwrap(); + let pathbuf = dir.path().to_path_buf(); + + generate_status_icon("my-other-project", "some-branch", BuildStatus::Error("Error Reason".to_string()), &pathbuf); + + let mut buf = dir.path().to_path_buf(); + buf.push("my-other-project"); + buf.push("some-branch.png"); + assert!(buf.exists()); + + let mut f = File::open(buf).unwrap(); + let mut buffer = Vec::new(); + f.read_to_end(&mut buffer).unwrap(); + + assert_eq!(buffer, ICON_ERROR); + } +}+ \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs @@ -13,6 +13,7 @@ mod execution; mod mail; mod crypto; mod variables; +mod dashboard; pub use crate::config::ExecutionConfig; @@ -20,6 +21,7 @@ use crate::config::{CinderellaConfig, Configs}; use crate::execution::{ExecutionResult, StepResult}; use crate::vcs::CodeSource; use crate::vcs::WorkingCopy; +use crate::dashboard::BuildStatus; fn random_dir(base_path: &str) -> PathBuf { let mut tempdir = PathBuf::from(base_path); @@ -79,6 +81,9 @@ pub fn run(exec_config: &ExecutionConfig) { let res = execution::execute(&pipelines, &variables); match res { + ExecutionResult::Success(_) => { + write_build_status(BuildStatus::Success, exec_config, &cinderella_config); + }, ExecutionResult::Error(steps) => { let mut output = String::new(); @@ -99,6 +104,8 @@ pub fn run(exec_config: &ExecutionConfig) { mailer.send_mail( &exec_config.name(), &format!("Build failed:\n\n{}", output)); + + write_build_status(BuildStatus::Error("Build failed".to_string()), exec_config, &cinderella_config); }, _ => (), } @@ -123,3 +130,16 @@ pub fn decrypt(cipherpath: &Path, plainpath: &Path, password: &str) { _ => println!("Cannot decrypt, probably wrong password"), } } + +fn write_build_status(status: BuildStatus, exec_config: &ExecutionConfig, cinderella_config: &CinderellaConfig) { + let branch = match &exec_config.branch { + Some(branch) => branch, + None => "master", + }; + + if let Some(dashboard) = &cinderella_config.dashboard { + let project = &exec_config.name(); + let outdir = PathBuf::from(&dashboard.folder); + dashboard::generate_status_icon(project, &branch, status, &outdir) + } +}+ \ No newline at end of file