diff --git a/Cargo.lock b/Cargo.lock index 733cad5..80d9985 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -498,9 +498,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" @@ -526,15 +526,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -543,21 +543,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-io", @@ -566,7 +566,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1295,12 +1294,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkcs1" version = "0.7.5" diff --git a/src/command/build/runner.rs b/src/command/build/runner.rs index d300f90..7ee9895 100644 --- a/src/command/build/runner.rs +++ b/src/command/build/runner.rs @@ -38,9 +38,11 @@ pub(crate) async fn run_build(args: BuildArgs) -> Result<()> { }; prepare_flake_repo(&config, target_config).await?; - build_target(&config, target_config).await?; + build_target(&db_handle, &config, target_config).await?; } + db_handle.conn.close().await; + Ok(()) } @@ -74,7 +76,11 @@ async fn prepare_flake_repo(config_root: &Config, target_config: &TargetConfig) Ok(()) } -async fn build_target(config_root: &Config, target_config: &TargetConfig) -> Result<()> { +async fn build_target( + db_handle: &DbHandle, + config_root: &Config, + target_config: &TargetConfig, +) -> Result<()> { let flake_directory = target_config.get_flake_directory(config_root)?; let build_directory = target_config.get_build_directory(config_root)?; assert_directory!( @@ -83,7 +89,13 @@ async fn build_target(config_root: &Config, target_config: &TargetConfig) -> Res build_directory.to_string_lossy() ); - nixos_build_target(build_directory, flake_directory, target_config.get_attr()?).await?; + nixos_build_target( + db_handle, + build_directory, + flake_directory, + target_config.get_attr()?, + ) + .await?; Ok(()) } diff --git a/src/database/db_handle.rs b/src/database/db_handle.rs index 8ca54de..7a88c40 100644 --- a/src/database/db_handle.rs +++ b/src/database/db_handle.rs @@ -9,6 +9,7 @@ use tracing::warn; use super::migration::run_migrations; use crate::Result; +#[derive(Debug, Clone)] pub(crate) struct DbHandle { pub(crate) conn: Pool, } diff --git a/src/nix_util/high_level.rs b/src/nix_util/high_level.rs index 2dabd54..3353b32 100644 --- a/src/nix_util/high_level.rs +++ b/src/nix_util/high_level.rs @@ -1,16 +1,25 @@ use std::ffi::OsStr; use std::ffi::OsString; use std::path::Path; +use std::process::Stdio; +use tokio::io::AsyncBufReadExt; +use tokio::io::BufReader; +use tokio::io::Lines; +use tokio::process::Child; +use tokio::process::ChildStderr; +use tokio::process::ChildStdout; use tokio::process::Command; -use crate::error::CustomError; +use crate::Result; +use crate::database::db_handle::DbHandle; pub(crate) async fn nixos_build_target( + db_handle: &DbHandle, build_path: B, flake_path: F, attr: A, -) -> Result<(), CustomError> +) -> Result<()> where B: AsRef, F: AsRef, @@ -29,6 +38,9 @@ where // nixos-rebuild build --show-trace --sudo --max-jobs "$JOBS" --flake "$DIR/../../#odo" --log-format internal-json -v "${@}" let mut command = Command::new("nixos-rebuild"); command.current_dir(build_path); + command.stdout(Stdio::piped()); + command.stderr(Stdio::piped()); + command.stdin(Stdio::null()); command.args([ "build", "--show-trace", @@ -38,21 +50,116 @@ where "--log-format", "internal-json", "-v", + "--keep-going", ]); command.arg("--flake"); command.arg(reference); command.kill_on_drop(true); let mut child = command.spawn()?; - let status = child.wait().await?; - if !status.success() { - return Err("Nixos rebuild failed.".into()); + + let mut output_stream = OutputSream::from_child(&mut child)?; + + let exit_status_handle = tokio::spawn(async move { + let status = child + .wait() + .await + .expect("nixos-rebuild encountered an error"); + status + }); + + loop { + let next_line = output_stream.next_line().await?; + match next_line { + OutputLine::Stdout(line) => { + println!("STDOUT: {line}"); + } + OutputLine::Stderr(line) => { + println!("STDERR: {line}"); + } + OutputLine::Done => break, + }; } - // tokio::spawn(async move { - // let status = child.wait().await.expect("mpv encountered an error"); - // println!("mpv status was: {}", status); - // }); + let exit_status = exit_status_handle.await?; + println!("nixos-rebuild status was: {}", exit_status); Ok(()) } + +struct OutputSream { + stdout: Option>>, + stderr: Option>>, +} + +impl OutputSream { + pub(crate) fn from_child(child: &mut Child) -> Result { + let stdout = child + .stdout + .take() + .expect("child did not have a handle to stdout"); + let stderr = child + .stderr + .take() + .expect("child did not have a handle to stderr"); + let stdout = BufReader::new(stdout).lines(); + let stderr = BufReader::new(stderr).lines(); + Ok(OutputSream { + stdout: Some(stdout), + stderr: Some(stderr), + }) + } + + pub(crate) async fn next_line(&mut self) -> Result { + loop { + match (&mut self.stdout, &mut self.stderr) { + (None, None) => { + return Ok(OutputLine::Done); + } + (None, Some(err)) => { + if let Some(line) = err.next_line().await? { + return Ok(OutputLine::Stderr(line)); + } else { + return Ok(OutputLine::Done); + } + } + (Some(out), None) => { + if let Some(line) = out.next_line().await? { + return Ok(OutputLine::Stdout(line)); + } else { + return Ok(OutputLine::Done); + } + } + (Some(out), Some(err)) => { + tokio::select! { + Ok(line) = out.next_line() => match line { + Some(line) => { + return Ok(OutputLine::Stdout(line)); + }, + None => { + self.stdout.take(); + }, + }, + Ok(line) = err.next_line() => match line { + Some(line) => { + return Ok(OutputLine::Stderr(line)); + }, + None => { + self.stderr.take(); + }, + }, + else => { + return Ok(OutputLine::Done); + }, + }; + } + }; + } + } +} + +enum OutputLine { + Stdout(String), + Stderr(String), + Done, +}