Repository: sharkdp/fd (rust)

Issue:
Regression: `--exec-batch` no longer preserves status code
Suppose you have a `fd` command targeting at least one file, e.g. a directory `/dir` containing a file `foo`. Then running
```bash
fd foo /dir -X bash -c 'exit 1'
```
succeeds (based on exit code) in 8.4.0, but fails in e.g. 8.3.2. I would expect the latter to be the correct behavior (since #477).

Bisecting shows that #960 (specifically 9fb0c5d372062d6193798519526cb7b14ea24fcc) caused this regression; happy to open a PR if desired. 

Pull request description:
Respect exit codes with `--exec-batch`
Closes #1136



Patch:
diff --git a/doc/release-checklist.md b/doc/release-checklist.md
deleted file mode 100644
index 9983304..0000000
--- a/doc/release-checklist.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# Release checklist
-
-This file can be used as-is, or copied into the GitHub PR description which includes
-necessary changes for the upcoming release.
-
-## Version bump
-
-- [ ] Create a new branch for the required changes for this release.
-- [ ] Update version in `Cargo.toml`. Run `cargo build` to update `Cargo.lock`.
-      Make sure to `git add` the `Cargo.lock` changes as well.
-- [ ] Find the current min. supported Rust version by running
-      `grep '^\s*MIN_SUPPORTED_RUST_VERSION' .github/workflows/CICD.yml`.
-- [ ] Update the `fd` version and the min. supported Rust version in `README.md`.
-- [ ] Update `CHANGELOG.md`. Change the heading of the *"Upcoming release"* section
-      to the version of this release.
-
-## Pre-release checks and updates
-
-- [ ] Install the latest version (`cargo install --locked -f --path .`) and make
-      sure that it is available on the `PATH` (`fd --version` should show the
-      new version).
-- [ ] Review `-h`, `--help`, and the `man` page.
-- [ ] Run `fd -h` and copy the output to the *"Command-line options"* section in
-      the README
-- [ ] Push all changes and wait for CI to succeed (before continuing with the
-      next section).
-- [ ] Optional: manually test the new features and command-line options described
-      in the `CHANGELOG.md`.
-- [ ] Run `cargo publish --dry-run` to make sure that it will succeed later
-      (after creating the GitHub release).
-
-## Release
-
-- [ ] Create a tag and push it: `git tag vX.Y.Z; git push origin tag vX.Y.Z`.
-      This will trigger the deployment via GitHub Actions.
-      REMINDER: If your `origin` is a fork, don't forget to push to e.g. `upstream`
-      instead.
-- [ ] Go to https://github.com/sharkdp/fd/releases/new to create the new
-      release. Select the new tag and also use it as the release title. For the
-      release notes, copy the corresponding section from `CHANGELOG.md` and
-      possibly add additional remarks for package maintainers.
-      Publish the release.
-- [ ] Check if the binary deployment works (archives and Debian packages should
-      appear when the CI run *for the Git tag* has finished).
-- [ ] Publish to crates.io by running `cargo publish` in a *clean* repository.
-      One way to do this is to clone a fresh copy.
-
-## Post-release
-
-- [ ] Prepare a new *"Upcoming release"* section at the top of `CHANGELOG.md`.
-      Put this at the top:
-      ```
-      # Upcoming release
-
-      ## Features
-
-      ## Bugfixes
-
-      ## Changes
-
-      ## Other
-
-      ```
diff --git a/src/exec/mod.rs b/src/exec/mod.rs
index da83223..33ddaae 100644
--- a/src/exec/mod.rs
+++ b/src/exec/mod.rs
@@ -16,7 +16,7 @@ use argmax::Command;
 use once_cell::sync::Lazy;
 use regex::Regex;
 
-use crate::exit_codes::ExitCode;
+use crate::exit_codes::{merge_exitcodes, ExitCode};
 
 use self::command::{execute_commands, handle_cmd_error};
 use self::input::{basename, dirname, remove_extension};
@@ -120,7 +120,7 @@ impl CommandSet {
                     }
                 }
 
-                ExitCode::Success
+                merge_exitcodes(builders.iter().map(|b| b.exit_code()))
             }
             Err(e) => handle_cmd_error(None, e),
         }
@@ -136,6 +136,7 @@ struct CommandBuilder {
     cmd: Command,
     count: usize,
     limit: usize,
+    exit_code: ExitCode,
 }
 
 impl CommandBuilder {
@@ -163,6 +164,7 @@ impl CommandBuilder {
             cmd,
             count: 0,
             limit,
+            exit_code: ExitCode::Success,
         })
     }
 
@@ -196,7 +198,9 @@ impl CommandBuilder {
     fn finish(&mut self) -> io::Result<()> {
         if self.count > 0 {
             self.cmd.try_args(&self.post_args)?;
-            self.cmd.status()?;
+            if !self.cmd.status()?.success() {
+                self.exit_code = ExitCode::GeneralError;
+            }
 
             self.cmd = Self::new_command(&self.pre_args)?;
             self.count = 0;
@@ -204,6 +208,10 @@ impl CommandBuilder {
 
         Ok(())
     }
+
+    fn exit_code(&self) -> ExitCode {
+        self.exit_code
+    }
 }
 
 /// Represents a template that is utilized to generate command strings.


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 50157c4..242d4bc 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -1496,6 +1496,8 @@ fn test_exec_batch() {
             &["foo", "--exec-batch", "echo {}"],
             "[fd error]: First argument of exec-batch is expected to be a fixed executable",
         );
+
+        te.assert_failure_with_error(&["a.foo", "--exec-batch", "bash", "-c", "exit 1"], "");
     }
 }
 
@@ -1551,6 +1553,20 @@ fn test_exec_batch_multi() {
             ],
         ]
     );
+
+    te.assert_failure_with_error(
+        &[
+            "a.foo",
+            "--exec-batch",
+            "echo",
+            ";",
+            "--exec-batch",
+            "bash",
+            "-c",
+            "exit 1",
+        ],
+        "",
+    );
 }
 
 #[test]