From 4fa33815af3a81dcd47b8e014fb112b3c3b93d96 Mon Sep 17 00:00:00 2001 From: Tsiry Sandratraina Date: Sat, 16 Mar 2024 21:18:24 +0000 Subject: [PATCH] feat: add git and http extension --- Cargo.lock | 310 +++++++++++++++++- crates/core/src/deps.rs | 48 +++ crates/ext/Cargo.toml | 2 + crates/ext/src/git.rs | 93 ++++++ crates/ext/src/http.rs | 94 ++++++ crates/ext/src/lib.rs | 2 + crates/graphql/Cargo.toml | 1 + crates/graphql/src/schema/directory.rs | 22 +- crates/graphql/src/schema/file.rs | 6 +- crates/graphql/src/schema/git.rs | 20 +- crates/graphql/src/schema/http.rs | 25 +- .../graphql/src/schema/objects/directory.rs | 234 ++++++++++++- crates/graphql/src/schema/objects/file.rs | 5 + crates/graphql/src/schema/objects/git.rs | 5 +- crates/graphql/src/schema/objects/pipeline.rs | 59 +++- crates/graphql/src/schema/pipeline.rs | 3 + 16 files changed, 900 insertions(+), 29 deletions(-) create mode 100644 crates/ext/src/git.rs create mode 100644 crates/ext/src/http.rs diff --git a/Cargo.lock b/Cargo.lock index 6551a0d..6bf7aa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,9 +352,9 @@ dependencies = [ [[package]] name = "async-graphql" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba89a35adbed833e0d21db467093a087c3a07b497626009eae02cb36f060567" +checksum = "261fa27d5bff5afdf7beff291b3bc73f99d1529804c70e51b0fbc51e70b1c6a9" dependencies = [ "async-graphql-derive", "async-graphql-parser", @@ -404,9 +404,9 @@ dependencies = [ [[package]] name = "async-graphql-derive" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9547f7f22688f022ea8001bdd57a1fce8996045dcb959b1730a79bafd366a9d9" +checksum = "3188809947798ea6db736715a60cf645ba3b87ea031c710130e1476b48e45967" dependencies = [ "Inflector", "async-graphql-parser", @@ -421,9 +421,9 @@ dependencies = [ [[package]] name = "async-graphql-parser" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8e3d4cc5074c46adfee619b5b6cf817943332f772ed5930ed78871f7dbbac6" +checksum = "d4e65a0b83027f35b2a5d9728a098bc66ac394caa8191d2c65ed9eb2985cf3d8" dependencies = [ "async-graphql-value", "pest", @@ -433,9 +433,9 @@ dependencies = [ [[package]] name = "async-graphql-value" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53ff1287a1283ea772b4099dd556ba4577d35f069ea4a93f337a525beefcfacf" +checksum = "68e40849c29a39012d38bff87bfed431f1ed6c53fbec493294c1045d61a7ae75" dependencies = [ "bytes", "indexmap 2.2.5", @@ -660,6 +660,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -881,6 +891,8 @@ dependencies = [ "anyhow", "fluentci-types", "owo-colors", + "reqwest", + "sha256", "users", ] @@ -891,6 +903,7 @@ dependencies = [ "anyhow", "async-graphql", "async-graphql-actix-web", + "dirs", "fluentci-core", "fluentci-ext", "fluentci-types", @@ -1086,6 +1099,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.12" @@ -1108,6 +1127,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" @@ -1120,6 +1150,44 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1180,6 +1248,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itoa" version = "1.0.10" @@ -1608,6 +1682,62 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1636,6 +1766,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -1654,6 +1815,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "semver" version = "1.0.22" @@ -1725,6 +1896,19 @@ dependencies = [ "digest", ] +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1779,18 +1963,18 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.25.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck", "proc-macro2", @@ -1821,6 +2005,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -1944,6 +2155,16 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -1975,6 +2196,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -1995,6 +2222,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -2037,6 +2270,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -2086,6 +2325,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2117,6 +2365,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -2146,6 +2406,22 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" @@ -2327,6 +2603,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/crates/core/src/deps.rs b/crates/core/src/deps.rs index a9e2ceb..e334c14 100644 --- a/crates/core/src/deps.rs +++ b/crates/core/src/deps.rs @@ -168,6 +168,54 @@ impl Graph { } } + pub fn execute_vertex(&mut self, id: &str) -> Result<(), String> { + let mut visited = vec![false; self.vertices.len()]; + let mut stack = Vec::new(); + let mut index = 0; + for (i, vertex) in self.vertices.iter().enumerate() { + if vertex.id == id { + index = i; + break; + } + } + stack.push(index); + while let Some(i) = stack.pop() { + if visited[i] { + continue; + } + visited[i] = true; + for edge in self.edges.iter().filter(|e| e.from == i) { + stack.push(edge.to); + } + let (tx, _rx) = mpsc::channel(); + + if self.vertices[i].label == "withWorkdir" { + if !Path::new(&self.vertices[i].command).exists() { + return Err(format!("Error: {}", self.vertices[i].id)); + } + self.work_dir = self.vertices[i].command.clone(); + continue; + } + + match self.vertices[i].run( + self.runner.clone(), + tx, + Output::Stdout, + false, + &self.work_dir, + ) { + Ok(status) => { + if !status.success() { + return Err(format!("Error: {}", self.vertices[i].id)); + } + } + Err(e) => { + return Err(format!("Error: {}", e)); + } + }; + } + Ok(()) + } pub fn size(&self) -> usize { self.vertices.len() } diff --git a/crates/ext/Cargo.toml b/crates/ext/Cargo.toml index be158ff..7771af1 100644 --- a/crates/ext/Cargo.toml +++ b/crates/ext/Cargo.toml @@ -12,4 +12,6 @@ version = "0.1.0" anyhow = "1.0.80" fluentci-types = {path = "../types"} owo-colors = "4.0.0" +reqwest = {version = "0.11.26", features = ["rustls-tls", "blocking"], default-features = false} +sha256 = "1.5.0" users = "0.11.0" diff --git a/crates/ext/src/git.rs b/crates/ext/src/git.rs new file mode 100644 index 0000000..05ad052 --- /dev/null +++ b/crates/ext/src/git.rs @@ -0,0 +1,93 @@ +use std::{ + io::{BufRead, BufReader}, + process::{Command, ExitStatus, Stdio}, + sync::mpsc::{self, Receiver, Sender}, + thread, +}; + +use crate::{pkgx::Pkgx, Extension}; +use anyhow::Error; +use fluentci_types::Output; + +#[derive(Default)] +pub struct Git {} + +impl Extension for Git { + fn exec( + &self, + url: &str, + tx: Sender, + out: Output, + last_cmd: bool, + work_dir: &str, + ) -> Result { + self.setup()?; + + if url.is_empty() { + return Ok(ExitStatus::default()); + } + + let (stdout_tx, stdout_rx): (Sender, Receiver) = mpsc::channel(); + let (stderr_tx, stderr_rx): (Sender, Receiver) = mpsc::channel(); + + let mut child = Command::new("bash") + .arg("-c") + .arg(format!("git clone {}", url)) + .current_dir(work_dir) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + let stdout_tx_clone = stdout_tx.clone(); + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + + let out_clone = out.clone(); + let tx_clone = tx.clone(); + + thread::spawn(move || { + let mut stdout = String::new(); + while let Ok(line) = stdout_rx.recv() { + println!("{}", line); + stdout.push_str(&line); + stdout.push_str("\n"); + } + if out_clone == Output::Stdout && last_cmd { + tx_clone.send(stdout).unwrap(); + } + }); + + thread::spawn(move || { + let mut stderr = String::new(); + while let Ok(line) = stderr_rx.recv() { + println!("{}", line); + stderr.push_str(&line); + stderr.push_str("\n"); + } + if out == Output::Stderr && last_cmd { + tx.send(stderr).unwrap(); + } + }); + + thread::spawn(move || { + let reader = BufReader::new(stdout); + for line in reader.lines() { + stdout_tx_clone.send(line.unwrap()).unwrap(); + } + }); + + thread::spawn(move || { + let reader = BufReader::new(stderr); + for line in reader.lines() { + stderr_tx.send(line.unwrap()).unwrap(); + } + }); + + child.wait().map_err(Error::from) + } + + fn setup(&self) -> Result<(), Error> { + Pkgx::default().install(vec!["git"])?; + Ok(()) + } +} diff --git a/crates/ext/src/http.rs b/crates/ext/src/http.rs new file mode 100644 index 0000000..7e1899a --- /dev/null +++ b/crates/ext/src/http.rs @@ -0,0 +1,94 @@ +use std::{ + io::{BufRead, BufReader}, + process::{Command, ExitStatus, Stdio}, + sync::mpsc::{self, Receiver, Sender}, + thread, +}; + +use crate::{pkgx::Pkgx, Extension}; +use anyhow::Error; +use fluentci_types::Output; + +#[derive(Default)] +pub struct Http {} + +impl Extension for Http { + fn exec( + &self, + url: &str, + tx: Sender, + out: Output, + last_cmd: bool, + work_dir: &str, + ) -> Result { + self.setup()?; + + if url.is_empty() { + return Ok(ExitStatus::default()); + } + + let (stdout_tx, stdout_rx): (Sender, Receiver) = mpsc::channel(); + let (stderr_tx, stderr_rx): (Sender, Receiver) = mpsc::channel(); + + let filename = sha256::digest(url).to_string(); + let mut child = Command::new("bash") + .arg("-c") + .arg(format!("curl -s {} -o {}", url, filename)) + .current_dir(work_dir) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + let stdout_tx_clone = stdout_tx.clone(); + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + + let out_clone = out.clone(); + let tx_clone = tx.clone(); + + thread::spawn(move || { + let mut stdout = String::new(); + while let Ok(line) = stdout_rx.recv() { + println!("{}", line); + stdout.push_str(&line); + stdout.push_str("\n"); + } + if out_clone == Output::Stdout && last_cmd { + tx_clone.send(stdout).unwrap(); + } + }); + + thread::spawn(move || { + let mut stderr = String::new(); + while let Ok(line) = stderr_rx.recv() { + println!("{}", line); + stderr.push_str(&line); + stderr.push_str("\n"); + } + if out == Output::Stderr && last_cmd { + tx.send(stderr).unwrap(); + } + }); + + thread::spawn(move || { + let reader = BufReader::new(stdout); + for line in reader.lines() { + stdout_tx_clone.send(line.unwrap()).unwrap(); + } + }); + + thread::spawn(move || { + let reader = BufReader::new(stderr); + for line in reader.lines() { + stderr_tx.send(line.unwrap()).unwrap(); + } + }); + + child.wait().map_err(Error::from) + } + + fn setup(&self) -> Result<(), Error> { + Pkgx::default().install(vec!["curl"])?; + Ok(()) + } +} diff --git a/crates/ext/src/lib.rs b/crates/ext/src/lib.rs index 3757596..17dffb9 100644 --- a/crates/ext/src/lib.rs +++ b/crates/ext/src/lib.rs @@ -7,6 +7,8 @@ pub mod devbox; pub mod devenv; pub mod envhub; pub mod flox; +pub mod git; +pub mod http; pub mod nix; pub mod pkgx; pub mod runner; diff --git a/crates/graphql/Cargo.toml b/crates/graphql/Cargo.toml index 7f414b9..34f4b5f 100644 --- a/crates/graphql/Cargo.toml +++ b/crates/graphql/Cargo.toml @@ -9,6 +9,7 @@ version = "0.1.0" anyhow = "1.0.80" async-graphql = "7.0.2" async-graphql-actix-web = "7.0.2" +dirs = "5.0.1" fluentci-core = {path = "../core", version = "0.1.0"} fluentci-ext = {path = "../ext", version = "0.1.0"} fluentci-types = {path = "../types", version = "0.1.0"} diff --git a/crates/graphql/src/schema/directory.rs b/crates/graphql/src/schema/directory.rs index 8fc32db..84616fc 100644 --- a/crates/graphql/src/schema/directory.rs +++ b/crates/graphql/src/schema/directory.rs @@ -1,6 +1,10 @@ -use std::sync::{Arc, Mutex}; +use std::{ + fs::canonicalize, + path::Path, + sync::{Arc, Mutex}, +}; -use super::objects::{cache::Cache, directory::Directory}; +use super::objects::directory::Directory; use async_graphql::{Context, Error, Object, ID}; use fluentci_core::deps::{Graph, GraphCommand}; use uuid::Uuid; @@ -10,9 +14,19 @@ pub struct DirectoryQuery; #[Object] impl DirectoryQuery { - async fn directory(&self, ctx: &Context<'_>) -> Result { + async fn directory(&self, ctx: &Context<'_>, path: String) -> Result { let graph = ctx.data::>>().unwrap(); let mut graph = graph.lock().unwrap(); + + if !Path::new(&path).exists() && !path.starts_with("/") { + let dir = canonicalize(".").unwrap(); + let dir = dir.to_str().unwrap(); + let dir = format!("{}/{}", dir, path); + return Err(Error::new(format!("Path `{}` does not exist", dir))); + } + + graph.work_dir = path.clone(); + let id = Uuid::new_v4().to_string(); graph.execute(GraphCommand::AddVertex( id.clone(), @@ -23,7 +37,7 @@ impl DirectoryQuery { drop(graph); - let directory = Directory { id: ID(id) }; + let directory = Directory { id: ID(id), path }; Ok(directory) } } diff --git a/crates/graphql/src/schema/file.rs b/crates/graphql/src/schema/file.rs index 4f190ad..b85e2aa 100644 --- a/crates/graphql/src/schema/file.rs +++ b/crates/graphql/src/schema/file.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use super::objects::{cache::Cache, directory::Directory, file::File}; +use super::objects::file::File; use async_graphql::{Context, Error, Object, ID}; use fluentci_core::deps::{Graph, GraphCommand}; use uuid::Uuid; @@ -10,7 +10,7 @@ pub struct FileQuery; #[Object] impl FileQuery { - async fn file(&self, ctx: &Context<'_>) -> Result { + async fn file(&self, ctx: &Context<'_>, path: String) -> Result { let graph = ctx.data::>>().unwrap(); let mut graph = graph.lock().unwrap(); let id = Uuid::new_v4().to_string(); @@ -23,7 +23,7 @@ impl FileQuery { drop(graph); - let file = File { id: ID(id) }; + let file = File { id: ID(id), path }; Ok(file) } } diff --git a/crates/graphql/src/schema/git.rs b/crates/graphql/src/schema/git.rs index f8dbae1..468205a 100644 --- a/crates/graphql/src/schema/git.rs +++ b/crates/graphql/src/schema/git.rs @@ -1,8 +1,12 @@ -use std::sync::{Arc, Mutex}; +use std::{ + fs, + sync::{Arc, Mutex}, +}; use super::objects::git::Git; use async_graphql::{Context, Error, Object, ID}; use fluentci_core::deps::{Graph, GraphCommand}; +use fluentci_ext::git::Git as GitExt; use uuid::Uuid; #[derive(Default, Clone)] @@ -10,16 +14,26 @@ pub struct GitQuery; #[Object] impl GitQuery { - async fn git(&self, ctx: &Context<'_>) -> Result { + async fn git(&self, ctx: &Context<'_>, url: String) -> Result { let graph = ctx.data::>>().unwrap(); let mut graph = graph.lock().unwrap(); + graph.reset(); + graph.runner = Arc::new(Box::new(GitExt::default())); + graph.runner.setup()?; + graph.work_dir = format!( + "{}/.fluentci/cache", + dirs::home_dir().unwrap().to_str().unwrap() + ); + fs::create_dir_all(&graph.work_dir)?; + let id = Uuid::new_v4().to_string(); graph.execute(GraphCommand::AddVertex( id.clone(), "git".into(), - "".into(), + url, vec![], )); + graph.execute_vertex(&id)?; drop(graph); diff --git a/crates/graphql/src/schema/http.rs b/crates/graphql/src/schema/http.rs index 907704e..c7ae83a 100644 --- a/crates/graphql/src/schema/http.rs +++ b/crates/graphql/src/schema/http.rs @@ -1,8 +1,12 @@ -use std::sync::{Arc, Mutex}; +use std::{ + fs, + sync::{Arc, Mutex}, +}; use super::objects::file::File; use async_graphql::{Context, Error, Object, ID}; use fluentci_core::deps::{Graph, GraphCommand}; +use fluentci_ext::http::Http as HttpExt; use uuid::Uuid; #[derive(Default, Clone)] @@ -10,19 +14,32 @@ pub struct HttpQuery; #[Object] impl HttpQuery { - async fn http(&self, ctx: &Context<'_>) -> Result { + async fn http(&self, ctx: &Context<'_>, url: String) -> Result { let graph = ctx.data::>>().unwrap(); let mut graph = graph.lock().unwrap(); + graph.reset(); + graph.runner = Arc::new(Box::new(HttpExt::default())); + graph.runner.setup()?; + graph.work_dir = format!( + "{}/.fluentci/cache", + dirs::home_dir().unwrap().to_str().unwrap() + ); + fs::create_dir_all(&graph.work_dir)?; + let id = Uuid::new_v4().to_string(); graph.execute(GraphCommand::AddVertex( id.clone(), "http".into(), - "".into(), + url, vec![], )); + graph.execute_vertex(&id)?; drop(graph); - let file = File { id: ID(id) }; + let file = File { + id: ID(id), + path: "/file".into(), + }; Ok(file) } } diff --git a/crates/graphql/src/schema/objects/directory.rs b/crates/graphql/src/schema/objects/directory.rs index 419704a..dff7ffc 100644 --- a/crates/graphql/src/schema/objects/directory.rs +++ b/crates/graphql/src/schema/objects/directory.rs @@ -1,8 +1,26 @@ -use async_graphql::{Error, Object, ID}; +use std::{ + fs::canonicalize, + path::Path, + sync::{mpsc::Receiver, Arc, Mutex}, +}; + +use async_graphql::{Context, Error, Object, ID}; +use fluentci_core::deps::{Graph, GraphCommand}; + +use fluentci_ext::devbox::Devbox as DevboxExt; +use fluentci_ext::devenv::Devenv as DevenvExt; +use fluentci_ext::flox::Flox as FloxExt; +use fluentci_ext::nix::Nix as NixExt; +use fluentci_ext::pkgx::Pkgx as PkgxExt; +use fluentci_types::Output; +use uuid::Uuid; + +use super::{devbox::Devbox, devenv::Devenv, flox::Flox, nix::Nix, pkgx::Pkgx}; #[derive(Debug, Clone, Default)] pub struct Directory { pub id: ID, + pub path: String, } #[Object] @@ -10,4 +28,218 @@ impl Directory { async fn id(&self) -> &ID { &self.id } + + async fn path(&self) -> &str { + &self.path + } + + async fn devbox(&self, ctx: &Context<'_>) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.runner = Arc::new(Box::new(DevboxExt::default())); + graph.runner.setup()?; + + let id = Uuid::new_v4().to_string(); + graph.execute(GraphCommand::AddVertex( + id.clone(), + "devbox".into(), + "".into(), + vec![], + )); + + let devbox = Devbox { id: ID(id) }; + Ok(devbox) + } + + async fn devenv(&self, ctx: &Context<'_>) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.runner = Arc::new(Box::new(DevenvExt::default())); + graph.runner.setup()?; + + let id = Uuid::new_v4().to_string(); + graph.execute(GraphCommand::AddVertex( + id.clone(), + "devenv".into(), + "".into(), + vec![], + )); + + let devenv = Devenv { id: ID(id) }; + Ok(devenv) + } + + async fn flox(&self, ctx: &Context<'_>) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.runner = Arc::new(Box::new(FloxExt::default())); + graph.runner.setup()?; + + let id = Uuid::new_v4().to_string(); + graph.execute(GraphCommand::AddVertex( + id.clone(), + "flox".into(), + "".into(), + vec![], + )); + + let flox = Flox { id: ID(id) }; + Ok(flox) + } + + async fn nix(&self, ctx: &Context<'_>) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.runner = Arc::new(Box::new(NixExt::default())); + graph.runner.setup()?; + + let id = Uuid::new_v4().to_string(); + + let dep_id = graph.vertices[graph.size() - 1].id.clone(); + let deps = match graph.size() { + 1 => vec![], + _ => vec![dep_id], + }; + + graph.execute(GraphCommand::AddVertex( + id.clone(), + "nix".into(), + "".into(), + deps, + )); + + if graph.size() > 2 { + let x = graph.size() - 2; + let y = graph.size() - 1; + graph.execute(GraphCommand::AddEdge(x, y)); + } + + let nix = Nix { id: ID(id) }; + Ok(nix) + } + + async fn pkgx(&self, ctx: &Context<'_>) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.runner = Arc::new(Box::new(PkgxExt::default())); + graph.runner.setup()?; + + let id = Uuid::new_v4().to_string(); + graph.execute(GraphCommand::AddVertex( + id.clone(), + "pkgx".into(), + "".into(), + vec![], + )); + + let pkgx = Pkgx { id: ID(id) }; + Ok(pkgx) + } + + async fn with_work_dir(&self, ctx: &Context<'_>, path: String) -> Result<&Directory, Error> { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + + if !Path::new(&path).exists() { + let dir = canonicalize(".").unwrap(); + let dir = dir.to_str().unwrap(); + let dir = format!("{}/{}", dir, path); + return Err(Error::new(format!("Path `{}` does not exist", dir))); + } + + let id = Uuid::new_v4().to_string(); + let dep_id = graph.vertices[graph.size() - 1].id.clone(); + let deps = match graph.size() { + 1 => vec![], + _ => vec![dep_id], + }; + graph.execute(GraphCommand::AddVertex( + id.clone(), + "withWorkdir".into(), + path, + deps, + )); + + if graph.size() > 2 { + let x = graph.size() - 2; + let y = graph.size() - 1; + graph.execute(GraphCommand::AddEdge(x, y)); + } + + Ok(self) + } + + async fn with_exec(&self, ctx: &Context<'_>, args: Vec) -> Result<&Directory, Error> { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + + let id = Uuid::new_v4().to_string(); + let dep_id = graph.vertices[graph.size() - 1].id.clone(); + let deps = match graph.size() { + 1 => vec![], + _ => vec![dep_id], + }; + graph.execute(GraphCommand::AddVertex( + id.clone(), + "exec".into(), + args.join(" "), + deps, + )); + + if graph.size() > 2 { + let x = graph.size() - 2; + let y = graph.size() - 1; + graph.execute(GraphCommand::AddEdge(x, y)); + } + + Ok(self) + } + + async fn with_service(&self, service: ID) -> Result<&Directory, Error> { + Ok(self) + } + + async fn with_cache(&self, cache: ID) -> Result<&Directory, Error> { + Ok(self) + } + + async fn stdout(&self, ctx: &Context<'_>) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.execute(GraphCommand::Execute(Output::Stdout)); + let rx = ctx.data::>>>().unwrap(); + let rx = rx.lock().unwrap(); + let (stdout, code) = rx.recv().unwrap(); + drop(rx); + drop(graph); + + if code != 0 { + return Err(Error::new(format!( + "Failed to execute command `{}`", + stdout + ))); + } + + Ok(stdout) + } + + async fn stderr(&self, ctx: &Context<'_>) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.execute(GraphCommand::Execute(Output::Stderr)); + let rx = ctx.data::>>>().unwrap(); + let rx = rx.lock().unwrap(); + let (stderr, code) = rx.recv().unwrap(); + drop(rx); + drop(graph); + + if code != 0 { + return Err(Error::new(format!( + "Failed to execute command `{}`", + stderr + ))); + } + + Ok(stderr) + } } diff --git a/crates/graphql/src/schema/objects/file.rs b/crates/graphql/src/schema/objects/file.rs index b0b01b0..6ffc354 100644 --- a/crates/graphql/src/schema/objects/file.rs +++ b/crates/graphql/src/schema/objects/file.rs @@ -3,6 +3,7 @@ use async_graphql::{Error, Object, ID}; #[derive(Debug, Clone, Default)] pub struct File { pub id: ID, + pub path: String, } #[Object] @@ -10,4 +11,8 @@ impl File { async fn id(&self) -> &ID { &self.id } + + async fn path(&self) -> &str { + &self.path + } } diff --git a/crates/graphql/src/schema/objects/git.rs b/crates/graphql/src/schema/objects/git.rs index f164a54..3493f30 100644 --- a/crates/graphql/src/schema/objects/git.rs +++ b/crates/graphql/src/schema/objects/git.rs @@ -20,7 +20,10 @@ impl Git { async fn tree(&self) -> Result { let id = Uuid::new_v4().to_string(); - let directory = Directory { id: ID(id) }; + let directory = Directory { + id: ID(id), + path: "/demo".into(), + }; Ok(directory) } } diff --git a/crates/graphql/src/schema/objects/pipeline.rs b/crates/graphql/src/schema/objects/pipeline.rs index a898ee1..4901f6f 100644 --- a/crates/graphql/src/schema/objects/pipeline.rs +++ b/crates/graphql/src/schema/objects/pipeline.rs @@ -1,5 +1,5 @@ use std::{ - fs::canonicalize, + fs::{self, canonicalize}, path::Path, sync::{mpsc::Receiver, Arc, Mutex}, }; @@ -9,11 +9,15 @@ use fluentci_core::deps::{Graph, GraphCommand}; use fluentci_ext::devbox::Devbox as DevboxExt; use fluentci_ext::devenv::Devenv as DevenvExt; use fluentci_ext::flox::Flox as FloxExt; +use fluentci_ext::git::Git as GitExt; +use fluentci_ext::http::Http as HttpExt; use fluentci_ext::nix::Nix as NixExt; use fluentci_ext::pkgx::Pkgx as PkgxExt; use fluentci_types::Output; use uuid::Uuid; +use crate::schema::objects::{file::File, git::Git}; + use super::{devbox::Devbox, devenv::Devenv, flox::Flox, nix::Nix, pkgx::Pkgx}; #[derive(Debug, Clone, Default)] @@ -27,6 +31,59 @@ impl Pipeline { &self.id } + async fn http(&self, ctx: &Context<'_>, url: String) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.runner = Arc::new(Box::new(HttpExt::default())); + graph.runner.setup()?; + graph.work_dir = format!( + "{}/.fluentci/cache", + dirs::home_dir().unwrap().to_str().unwrap() + ); + fs::create_dir_all(&graph.work_dir)?; + + let id = Uuid::new_v4().to_string(); + graph.execute(GraphCommand::AddVertex( + id.clone(), + "http".into(), + url, + vec![], + )); + graph.execute_vertex(&id)?; + + drop(graph); + let file = File { + id: ID(id), + path: "/file".into(), + }; + Ok(file) + } + + async fn git(&self, ctx: &Context<'_>, url: String) -> Result { + let graph = ctx.data::>>().unwrap(); + let mut graph = graph.lock().unwrap(); + graph.runner = Arc::new(Box::new(GitExt::default())); + graph.runner.setup()?; + graph.work_dir = format!( + "{}/.fluentci/cache", + dirs::home_dir().unwrap().to_str().unwrap() + ); + fs::create_dir_all(&graph.work_dir)?; + + let id = Uuid::new_v4().to_string(); + graph.execute(GraphCommand::AddVertex( + id.clone(), + "git".into(), + url, + vec![], + )); + graph.execute_vertex(&id)?; + drop(graph); + + let git = Git { id: ID(id) }; + Ok(git) + } + async fn devbox(&self, ctx: &Context<'_>) -> Result { let graph = ctx.data::>>().unwrap(); let mut graph = graph.lock().unwrap(); diff --git a/crates/graphql/src/schema/pipeline.rs b/crates/graphql/src/schema/pipeline.rs index 4a968a0..fca0df3 100644 --- a/crates/graphql/src/schema/pipeline.rs +++ b/crates/graphql/src/schema/pipeline.rs @@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex}; use super::objects::pipeline::Pipeline; use async_graphql::{Context, Error, Object, ID}; use fluentci_core::deps::{Graph, GraphCommand}; +use fluentci_ext::runner::Runner; use uuid::Uuid; #[derive(Default, Clone)] @@ -15,6 +16,8 @@ impl PipelineQuery { let mut graph = graph.lock().unwrap(); graph.reset(); + graph.runner = Arc::new(Box::new(Runner::default())); + graph.runner.setup()?; let id = Uuid::new_v4().to_string(); graph.execute(GraphCommand::AddVertex(