cinderella

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

commit e69e4526aae66ea89f607fa53e3f7fadbf5d6ee5
parent ca020108f463f1b847b0b27f4cb50f0578da07be
Author: Stefan Koch <programming@stefan-koch.name>
Date:   Sat, 14 Sep 2019 17:11:59 +0200

implement execution of TOML configuration

Diffstat:
MCargo.lock | 23+++++++++++++++++++++++
MCargo.toml | 1+
Asrc/execution.rs | 27+++++++++++++++++++++++++++
Asrc/lib.rs | 40++++++++++++++++++++++++++++++++++++++++
Msrc/main.rs | 62++------------------------------------------------------------
Asrc/pipeline.rs | 37+++++++++++++++++++++++++++++++++++++
Asrc/vcs.rs | 37+++++++++++++++++++++++++++++++++++++
7 files changed, 167 insertions(+), 60 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -33,6 +33,7 @@ version = "0.1.0" dependencies = [ "git2 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -117,6 +118,11 @@ dependencies = [ ] [[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -199,11 +205,25 @@ dependencies = [ ] [[package]] +name = "serde" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "toml" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -253,6 +273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum libgit2-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2078aec6f4b16d1b89f6a72e4f6eb1e75ffa85312023291e89c6d3087bc8fb" "checksum libssh2-sys 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "126a1f4078368b163bfdee65fbab072af08a1b374a5551b21e87ade27b1fbf9d" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" @@ -264,7 +285,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" diff --git a/Cargo.toml b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" [dependencies] git2 = "0.10.0" rand = "0.7.0" +toml = { version = "0.5", features = ["preserve_order"] } diff --git a/src/execution.rs b/src/execution.rs @@ -0,0 +1,27 @@ +use std::process::Command; + +use crate::pipeline; + +pub fn execute(pipelines: &Vec<pipeline::Pipeline>) { + for pipeline in pipelines { + execute_pipeline(pipeline); + } +} + +pub fn execute_pipeline(pipeline: &pipeline::Pipeline) { + println!("Executing Pipeline \"{}\"", pipeline.name); + + for cmd in &pipeline.commands { + // TODO: Successful argument parsing needs a lot more details, + // e.g. for quoted arguments like myprogram "argument 1" + // but for a first shot this works + let parts: Vec<&str> = cmd.split(" ").collect(); + + let status = Command::new(parts[0]) + .args(&parts[1..]) + .status() + .expect(&format!("failed to run {}", cmd)); + + assert!(status.success()); + } +} diff --git a/src/lib.rs b/src/lib.rs @@ -0,0 +1,40 @@ +use std::path::PathBuf; + +use rand::Rng; +use rand::distributions::Alphanumeric; + +mod vcs; +mod pipeline; +mod execution; + +use crate::vcs::CodeSource; + +fn random_dir() -> String { + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(10) + .collect::<String>() +} + +pub fn run(repo_url: &str) { + let repo = vcs::Git { + src: String::from(repo_url), + }; + + // generate a temp unique work dir + let mut tempdir = PathBuf::from("/tmp/cinderella"); + tempdir.push(random_dir()); + + let workdir = repo.fetch(&tempdir).expect("could not clone repo"); + + println!("Workdir is at {:?}", workdir.path); + + let mut cinderella_file = workdir.path.clone(); + cinderella_file.push(".cinderella.toml"); + + if let Some(pipelines) = pipeline::load_pipeline(".cinderella.toml") { + execution::execute(&pipelines); + } else { + println!("No Cinderella configuration found"); + } +} diff --git a/src/main.rs b/src/main.rs @@ -1,62 +1,4 @@ -use std::error::Error; -use std::fs; -use std::path::{Path, PathBuf}; -use git2::Repository; - -use rand::Rng; -use rand::distributions::Alphanumeric; - -// TODO: Move this to a sourcecontrol/git module later -trait CodeSource { - fn fetch(&self, target: &Path) -> Result<WorkDir, Box<Error>>; -} - -struct Git { - src: String, -} - -struct WorkDir { - path: PathBuf, -} - -impl CodeSource for Git { - fn fetch(&self, target: &Path) -> Result<WorkDir, Box<Error>> { - let repo = Repository::clone(&self.src, target)?; - let path = repo.workdir() - .expect("Newly cloned repo is expected to have a workdir"); - - Ok(WorkDir { - path: path.to_path_buf(), - }) - } -} - -impl Drop for WorkDir { - fn drop(&mut self) { - // TODO: Only write this error to a log file, but do not panic - fs::remove_dir_all(&self.path) - .expect("Could not delete work dir"); - } -} - -fn random_dir() -> String { - rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(10) - .collect::<String>() -} - fn main() { - println!("Hello, Cinderella!"); - - let repo = Git { - src: String::from("https://github.com/aufziehvogel/CInderella.git"), - }; - - // generate a temp unique work dir - let mut tempdir = PathBuf::from("/tmp/cinderella"); - tempdir.push(random_dir()); - - let workdir = repo.fetch(&tempdir).expect("could not clone repo"); - println!("Workdir is at {:?}", workdir.path); + // TODO: Entrypoint must be flexible + cinderella::run("https://github.com/aufziehvogel/CInderella.git") } diff --git a/src/pipeline.rs b/src/pipeline.rs @@ -0,0 +1,37 @@ +use std::fs; +use toml::Value; + +#[derive(Debug)] +pub struct Pipeline { + pub name: String, + pub commands: Vec<String>, +} + +pub fn load_pipeline(path: &str) -> Option<Vec<Pipeline>> { + if let Ok(contents) = fs::read_to_string(path) { + let data = contents.parse::<Value>().unwrap(); + + let res: Vec<Pipeline> = data.as_table().unwrap().iter() + .filter_map(|(key, value)| { + if value.is_table() { + // TODO: Better handle unwrap here and return error when + // file is invalid + Some(Pipeline { + name: key.to_string(), + commands: value["commands"].as_array().unwrap().iter() + .map(|cmd| String::from(cmd.as_str().unwrap())) + .collect(), + }) + } else { + None + } + }).collect(); + + Some(res) + } else { + // TODO: Should we differentiate more? Like: + // - file does not exist: None + // - file does exist, but cannot be read: Error + None + } +} diff --git a/src/vcs.rs b/src/vcs.rs @@ -0,0 +1,37 @@ +use std::error::Error; +use std::fs; +use std::path::{Path, PathBuf}; + +use git2::Repository; + +pub trait CodeSource { + fn fetch(&self, target: &Path) -> Result<WorkDir, Box<Error>>; +} + +pub struct Git { + pub src: String, +} + +pub struct WorkDir { + pub path: PathBuf, +} + +impl CodeSource for Git { + fn fetch(&self, target: &Path) -> Result<WorkDir, Box<Error>> { + let repo = Repository::clone(&self.src, target)?; + let path = repo.workdir() + .expect("Newly cloned repo is expected to have a workdir"); + + Ok(WorkDir { + path: path.to_path_buf(), + }) + } +} + +impl Drop for WorkDir { + fn drop(&mut self) { + // TODO: Only write this error to a log file, but do not panic + fs::remove_dir_all(&self.path) + .expect("Could not delete work dir"); + } +}