From 30b43aa093cc6a87f14f1338e574d4626b0f7480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= Date: Mon, 7 Oct 2024 14:06:13 +0100 Subject: [PATCH] tasks: preserve order of outputs --- devenv-tasks/src/lib.rs | 65 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/devenv-tasks/src/lib.rs b/devenv-tasks/src/lib.rs index 69c703a53..4ab09fe6b 100644 --- a/devenv-tasks/src/lib.rs +++ b/devenv-tasks/src/lib.rs @@ -3,6 +3,9 @@ use miette::Diagnostic; use petgraph::algo::toposort; use petgraph::graph::{DiGraph, NodeIndex}; use petgraph::visit::EdgeRef; + +use std::collections::BTreeMap; + use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::fmt::Display; @@ -84,7 +87,7 @@ pub struct Config { } #[derive(Serialize)] -pub struct Outputs(HashMap); +pub struct Outputs(BTreeMap); #[derive(Debug, Clone)] pub struct Output(Option); @@ -97,9 +100,8 @@ impl TryFrom for Config { } type LinesOutput = Vec<(std::time::Instant, String)>; - impl std::ops::Deref for Outputs { - type Target = HashMap; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 @@ -160,7 +162,7 @@ impl TaskState { fn prepare_command( &self, cmd: &str, - outputs: &HashMap, + outputs: &BTreeMap, ) -> (Command, tempfile::NamedTempFile) { let mut command = Command::new(cmd); command.stdout(Stdio::piped()).stderr(Stdio::piped()); @@ -219,7 +221,7 @@ impl TaskState { async fn run( &self, now: Instant, - outputs: &HashMap, + outputs: &BTreeMap, ) -> TaskCompleted { if let Some(cmd) = &self.task.status { let (mut command, outputs_file) = self.prepare_command(cmd, outputs); @@ -510,7 +512,7 @@ impl Tasks { #[instrument(skip(self))] async fn run(&self) -> Outputs { let mut running_tasks = JoinSet::new(); - let outputs = Arc::new(Mutex::new(HashMap::new())); + let outputs = Arc::new(Mutex::new(BTreeMap::new())); for index in &self.tasks_order { let task_state = &self.graph[*index]; @@ -1235,6 +1237,56 @@ mod test { Ok(()) } + #[tokio::test] + async fn test_output_order() -> Result<(), Error> { + let script1 = create_script( + r#"#!/bin/sh +echo '{"key": "value1"}' > $DEVENV_TASK_OUTPUT_FILE +"#, + )?; + let script2 = create_script( + r#"#!/bin/sh +echo '{"key": "value2"}' > $DEVENV_TASK_OUTPUT_FILE +"#, + )?; + let script3 = create_script( + r#"#!/bin/sh +echo '{"key": "value3"}' > $DEVENV_TASK_OUTPUT_FILE +"#, + )?; + + let tasks = Tasks::new( + Config::try_from(json!({ + "roots": ["myapp:task_3"], + "tasks": [ + { + "name": "myapp:task_1", + "command": script1.to_str().unwrap(), + }, + { + "name": "myapp:task_2", + "command": script2.to_str().unwrap(), + "after": ["myapp:task_1"], + }, + { + "name": "myapp:task_3", + "command": script3.to_str().unwrap(), + "after": ["myapp:task_2"], + } + ] + })) + .unwrap(), + ) + .await?; + + let outputs = tasks.run().await; + + let keys: Vec<_> = outputs.keys().collect(); + assert_eq!(keys, vec!["myapp:task_1", "myapp:task_2", "myapp:task_3"]); + + Ok(()) + } + #[tokio::test] async fn test_inputs_outputs() -> Result<(), Error> { let input_script = create_script( @@ -1327,4 +1379,3 @@ fi Ok(temp_file.into_temp_path()) } } -