Skip to content

Commit

Permalink
feat: added keep-order task output type
Browse files Browse the repository at this point in the history
Fixes #2347
  • Loading branch information
jdx committed Dec 21, 2024
1 parent 0909a98 commit de9888e
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 49 deletions.
11 changes: 11 additions & 0 deletions e2e/tasks/test_task_keep_order
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

cat <<EOF >mise.toml
tasks.a = "echo a"
tasks.b = "echo b"
tasks.c = "echo c"
tasks.all.depends = ['a', 'b', 'c']
EOF
assert "mise run -o keep-order all" "[a] a
[b] b
[c] c"
14 changes: 13 additions & 1 deletion settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,19 @@ enum = [
[
"interleave",
"(default if jobs == 1 or all tasks run sequentially) print output as it comes in"
]
],
[
"keep-order",
"print output from tasks in the order they are defined"
],
[
"quiet",
"print only stdout/stderr from tasks and nothing from mise",
],
[
"silent",
"print nothing from tasks or mise",
],
]
docs = """
Change output style when executing tasks. This controls the output of `mise run`.
Expand Down
7 changes: 4 additions & 3 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub enum LevelFilter {
Error,
}

#[derive(clap::Parser, Debug)]
#[derive(clap::Parser)]
#[clap(name = "mise", about, long_about = LONG_ABOUT, after_long_help = AFTER_LONG_HELP, author = "Jeff Dickey <@jdx>", arg_required_else_help = true)]
pub struct Cli {
#[clap(subcommand)]
Expand Down Expand Up @@ -160,7 +160,7 @@ pub struct Cli {
pub global_output_flags: CliGlobalOutputFlags,
}

#[derive(Debug, clap::Args)]
#[derive(clap::Args)]
#[group(multiple = false)]
pub struct CliGlobalOutputFlags {
/// Sets log level to debug
Expand All @@ -182,7 +182,7 @@ pub struct CliGlobalOutputFlags {
pub verbose: u8,
}

#[derive(Debug, Subcommand, strum::Display)]
#[derive(Subcommand, strum::Display)]
#[strum(serialize_all = "kebab-case")]
pub enum Commands {
Activate(activate::Activate),
Expand Down Expand Up @@ -388,6 +388,7 @@ impl Cli {
timings: self.timings,
tmpdir: Default::default(),
tool: Default::default(),
keep_order_output: Default::default(),
}));
} else if let Some(cmd) = external::COMMANDS.get(&task) {
external::execute(
Expand Down
81 changes: 75 additions & 6 deletions src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use demand::{DemandOption, Select};
use duct::IntoExecutablePath;
use eyre::{bail, ensure, eyre, Result};
use glob::glob;
use indexmap::IndexMap;
use itertools::Itertools;
#[cfg(unix)]
use nix::sys::signal::SIGTERM;
Expand Down Expand Up @@ -57,7 +58,7 @@ use xx::regex;
/// npm run build
/// EOF
/// $ mise run build
#[derive(Debug, clap::Args)]
#[derive(clap::Args)]
#[clap(visible_alias = "r", verbatim_doc_comment, disable_help_flag = true, after_long_help = AFTER_LONG_HELP)]
pub struct Run {
/// Tasks to run
Expand Down Expand Up @@ -172,15 +173,21 @@ pub struct Run {
///
/// - `prefix` - Print stdout/stderr by line, prefixed with the task's label
/// - `interleave` - Print directly to stdout/stderr instead of by line
/// - `keep-order` - Print stdout/stderr by line, prefixed with the task's label, but keep the order of the output
/// - `quiet` - Don't show extra output
/// - `silent` - Don't show any output including stdout and stderr from the task except for errors
#[clap(short, long, env = "MISE_TASK_OUTPUT")]
pub output: Option<TaskOutput>,

#[clap(skip)]
pub tmpdir: PathBuf,

#[clap(skip)]
pub keep_order_output: Mutex<IndexMap<Task, KeepOrderOutputs>>,
}

type KeepOrderOutputs = (Vec<(String, String)>, Vec<(String, String)>);

impl Run {
pub fn run(mut self) -> Result<()> {
if self.task == "-h" {
Expand Down Expand Up @@ -222,6 +229,12 @@ impl Run {
let tasks = Deps::new(tasks)?;
for task in tasks.all() {
self.validate_task(task)?;
if self.output(Some(task)) == TaskOutput::KeepOrder {
self.keep_order_output
.lock()
.unwrap()
.insert(task.clone(), Default::default());
}
}

let num_tasks = tasks.all().count();
Expand Down Expand Up @@ -311,7 +324,27 @@ impl Run {
eprintln!("{}", style::edim(msg));
};

time!("paralellize_tasks done");
if self.output(None) == TaskOutput::KeepOrder {
// TODO: display these as tasks complete in order somehow rather than waiting until everything is done
let output = self.keep_order_output.lock().unwrap();
for (out, err) in output.values() {
for (prefix, line) in out {
if console::colors_enabled() {
prefix_println!(prefix, "{line}\x1b[0m");
} else {
prefix_println!(prefix, "{line}");
}
}
for (prefix, line) in err {
if console::colors_enabled_stderr() {
prefix_eprintln!(prefix, "{line}\x1b[0m");
} else {
prefix_eprintln!(prefix, "{line}");
}
}
}
}
time!("parallelize_tasks done");

Ok(())
}
Expand Down Expand Up @@ -538,7 +571,42 @@ impl Run {
.raw(self.raw(Some(task)));
cmd.with_pass_signals();
match self.output(Some(task)) {
TaskOutput::Prefix => cmd = cmd.prefix(prefix),
TaskOutput::Prefix => {
cmd = cmd.with_on_stdout(|line| {
if console::colors_enabled() {
prefix_println!(prefix, "{line}\x1b[0m");
} else {
prefix_println!(prefix, "{line}");
}
});
cmd = cmd.with_on_stderr(|line| {
if console::colors_enabled() {
prefix_eprintln!(prefix, "{line}\x1b[0m");
} else {
prefix_eprintln!(prefix, "{line}");
}
});
}
TaskOutput::KeepOrder => {
cmd = cmd.with_on_stdout(|line| {
self.keep_order_output
.lock()
.unwrap()
.get_mut(task)
.unwrap()
.0
.push((prefix.to_string(), line));
});
cmd = cmd.with_on_stderr(|line| {
self.keep_order_output
.lock()
.unwrap()
.get_mut(task)
.unwrap()
.1
.push((prefix.to_string(), line));
});
}
TaskOutput::Silent => {
cmd = cmd.stdout(Stdio::null()).stderr(Stdio::null());
}
Expand Down Expand Up @@ -841,12 +909,13 @@ static AFTER_LONG_HELP: &str = color_print::cstr!(
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum TaskOutput {
Interleave,
KeepOrder,
#[default]
Prefix,
Interleave,
Quiet,
Silent,
}
Expand Down
4 changes: 2 additions & 2 deletions src/cli/tasks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod info;
mod ls;

/// Manage tasks
#[derive(Debug, clap::Args)]
#[derive(clap::Args)]
#[clap(visible_alias = "t", alias = "task", verbatim_doc_comment)]
pub struct Tasks {
#[clap(subcommand)]
Expand All @@ -23,7 +23,7 @@ pub struct Tasks {
ls: ls::TasksLs,
}

#[derive(Debug, Subcommand)]
#[derive(Subcommand)]
enum Commands {
Add(add::TasksAdd),
Deps(deps::TasksDeps),
Expand Down
63 changes: 33 additions & 30 deletions src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,11 @@ pub struct CmdLineRunner<'a> {
cmd: Command,
pr: Option<&'a dyn SingleReport>,
stdin: Option<String>,
prefix: String,
redactions: IndexSet<String>,
raw: bool,
pass_signals: bool,
on_stdout: Option<Box<dyn Fn(String) + 'a>>,
on_stderr: Option<Box<dyn Fn(String) + 'a>>,
}

static OUTPUT_LOCK: Mutex<()> = Mutex::new(());
Expand All @@ -127,10 +128,11 @@ impl<'a> CmdLineRunner<'a> {
cmd,
pr: None,
stdin: None,
prefix: String::new(),
redactions: Default::default(),
raw: false,
pass_signals: false,
on_stdout: None,
on_stderr: None,
}
}

Expand Down Expand Up @@ -184,8 +186,13 @@ impl<'a> CmdLineRunner<'a> {
self
}

pub fn prefix(mut self, prefix: impl Into<String>) -> Self {
self.prefix = prefix.into();
pub fn with_on_stdout<F: Fn(String) + 'a>(mut self, on_stdout: F) -> Self {
self.on_stdout = Some(Box::new(on_stdout));
self
}

pub fn with_on_stderr<F: Fn(String) + 'a>(mut self, on_stderr: F) -> Self {
self.on_stderr = Some(Box::new(on_stderr));
self
}

Expand Down Expand Up @@ -347,10 +354,18 @@ impl<'a> CmdLineRunner<'a> {
for line in rx {
match line {
ChildProcessOutput::Stdout(line) => {
let line = self
.redactions
.iter()
.fold(line, |acc, r| acc.replace(r, "[redacted]"));
self.on_stdout(line.clone());
combined_output.push(line);
}
ChildProcessOutput::Stderr(line) => {
let line = self
.redactions
.iter()
.fold(line, |acc, r| acc.replace(r, "[redacted]"));
self.on_stderr(line.clone());
combined_output.push(line);
}
Expand Down Expand Up @@ -387,35 +402,29 @@ impl<'a> CmdLineRunner<'a> {
}
}

fn on_stdout(&self, mut line: String) {
line = self
.redactions
.iter()
.fold(line, |acc, r| acc.replace(r, "[redacted]"));
fn on_stdout(&self, line: String) {
let _lock = OUTPUT_LOCK.lock().unwrap();
if let Some(on_stdout) = &self.on_stdout {
on_stdout(line);
return;
}
if let Some(pr) = self.pr {
if !line.trim().is_empty() {
pr.set_message(line)
}
} else if console::colors_enabled() {
if self.prefix.is_empty() {
println!("{line}\x1b[0m");
} else {
prefix_println!(self.prefix, "{line}\x1b[0m");
}
} else if self.prefix.is_empty() {
println!("{line}");
println!("{line}\x1b[0m");
} else {
prefix_println!(self.prefix, "{line}");
println!("{line}");
}
}

fn on_stderr(&self, mut line: String) {
line = self
.redactions
.iter()
.fold(line, |acc, r| acc.replace(r, "[redacted]"));
fn on_stderr(&self, line: String) {
let _lock = OUTPUT_LOCK.lock().unwrap();
if let Some(on_stderr) = &self.on_stderr {
on_stderr(line);
return;
}
match self.pr {
Some(pr) => {
if !line.trim().is_empty() {
Expand All @@ -424,15 +433,9 @@ impl<'a> CmdLineRunner<'a> {
}
None => {
if console::colors_enabled_stderr() {
if self.prefix.is_empty() {
eprintln!("{line}\x1b[0m");
} else {
prefix_eprintln!(self.prefix, "{line}\x1b[0m");
}
} else if self.prefix.is_empty() {
eprintln!("{line}");
eprintln!("{line}\x1b[0m");
} else {
prefix_eprintln!(self.prefix, "{line}");
eprintln!("{line}");
}
}
}
Expand Down
Loading

0 comments on commit de9888e

Please sign in to comment.