feat!: report findings with exit code 4, not the code for failure

This commit is contained in:
2026-09-08 15:05:00 +00:00
parent 23b7fde0ea
commit d7a54c70f9
3 changed files with 17 additions and 21 deletions

View File

@@ -45,7 +45,7 @@ pub enum Link {
#[arg(long)] #[arg(long)]
porcelain: bool, porcelain: bool,
/// Exit with 1 when anything is outside the store, for scripts /// Exit with 4 when anything is outside the store, for scripts
#[arg(long)] #[arg(long)]
exit_code: bool, exit_code: bool,

View File

@@ -1,6 +1,5 @@
use fs_err::{read_dir, read_link}; use fs_err::{read_dir, read_link};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::ExitCode;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
@@ -9,14 +8,14 @@ use crate::cmd::{Cmd, LsFiles};
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::output::{line, text}; use crate::output::{line, text};
// whether anything is outside the store, which main turns into an exit code
pub fn check( pub fn check(
ctx: &Ctx, ctx: &Ctx,
paths: &[PathBuf], paths: &[PathBuf],
porcelain: bool, porcelain: bool,
null: bool, null: bool,
exit_code: bool,
store: Option<&Path>, store: Option<&Path>,
) -> Result<ExitCode> { ) -> Result<bool> {
let repo = Repo::discover(ctx, store)?; let repo = Repo::discover(ctx, store)?;
let pathspecs = relative_pathspecs(&repo, paths)?; let pathspecs = relative_pathspecs(&repo, paths)?;
@@ -43,19 +42,12 @@ pub fn check(
print_listing(&repo, &exposed); print_listing(&repo, &exposed);
} }
// git's --exit-code convention: nothing to report is 0, anything is 1 Ok(!exposed.is_empty())
if exit_code && !exposed.is_empty() {
return Ok(ExitCode::FAILURE);
}
Ok(ExitCode::SUCCESS)
} }
fn print_porcelain(exposed: &[Exposed], null: bool) { fn print_porcelain(exposed: &[Exposed], null: bool) {
let end = if null { '\0' } else { '\n' }; let end = if null { '\0' } else { '\n' };
// a filename can hold an arrow but not a NUL, so -z separates the two paths // a filename can hold an arrow but not a NUL, as `git status -z` also assumes
// with one the way `git status -z` does for a rename. the two character code
// stays where it is: it cannot be mistaken for part of a path
let between = if null { "\0" } else { " -> " }; let between = if null { "\0" } else { " -> " };
for item in exposed { for item in exposed {

View File

@@ -14,6 +14,9 @@ use clap::Parser;
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::output::note; use crate::output::note;
// 0 ran with nothing to report, 1 could not finish, 2 bad arguments, 4 found something
const FINDINGS: u8 = 4;
fn main() -> ExitCode { fn main() -> ExitCode {
let args = cli::Ahab::parse(); let args = cli::Ahab::parse();
@@ -87,14 +90,15 @@ fn run(ctx: &Ctx, command: cli::Commands) -> Result<ExitCode> {
null, null,
exit_code, exit_code,
store, store,
} => commands::link::check( } => {
ctx, let exposed =
&paths, commands::link::check(ctx, &paths, porcelain, null, store.root.as_deref())?;
porcelain,
null, match exit_code && exposed {
exit_code, true => Ok(ExitCode::from(FINDINGS)),
store.root.as_deref(), false => Ok(done),
), }
}
}, },
cli::Commands::Status { store } => { cli::Commands::Status { store } => {
commands::status::status(ctx, store.root.as_deref())?; commands::status::status(ctx, store.root.as_deref())?;