feat(xatu): set CLI exit statuses

This commit is contained in:
2026-09-19 16:58:27 -04:00
parent 58f2350d5f
commit 5e1a9fc036
3 changed files with 79 additions and 10 deletions

View File

@@ -41,6 +41,17 @@ rust_test(
], ],
) )
sh_test(
name = "xatu_cli_test",
srcs = ["xatu_cli_test.sh"],
data = [
":xatu",
"testdata/clean.nix",
"testdata/invalid.nix",
"testdata/lint_findings.nix",
],
)
rust_binary( rust_binary(
name = "xatu", name = "xatu",
crate_name = "xatu_cli", crate_name = "xatu_cli",

38
src/xatu/xatu_cli_test.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
xatu="$TEST_SRCDIR/$TEST_WORKSPACE/src/xatu/xatu"
fixture="$TEST_SRCDIR/$TEST_WORKSPACE/src/xatu/testdata"
tmp="${TEST_TMPDIR:-$(mktemp -d)}"
run_xatu() {
local name="$1"
local expected_status="$2"
shift 2
set +e
"$xatu" "$@" >"$tmp/$name.stdout" 2>"$tmp/$name.stderr"
local actual_status="$?"
set -e
if [[ "$actual_status" != "$expected_status" ]]; then
echo "expected $name to exit $expected_status, got $actual_status" >&2
echo "stdout:" >&2
cat "$tmp/$name.stdout" >&2
echo "stderr:" >&2
cat "$tmp/$name.stderr" >&2
exit 1
fi
}
cp "$fixture/clean.nix" "$tmp/clean.nix"
cp "$fixture/invalid.nix" "$tmp/invalid.nix"
cp "$fixture/lint_findings.nix" "$tmp/lint_findings.nix"
mkdir -p "$tmp/project/testdata"
cp "$fixture/invalid.nix" "$tmp/project/testdata/invalid.nix"
run_xatu clean 0 "$tmp/clean.nix"
run_xatu findings 1 "$tmp/lint_findings.nix"
run_xatu fatal 2 "$tmp/invalid.nix"
run_xatu ignores_testdata_by_default 0 "$tmp/project"
run_xatu checks_explicit_testdata_path 2 "$tmp/project/testdata/invalid.nix"

View File

@@ -1,7 +1,8 @@
use clap::Parser; use clap::Parser;
use status::ResultExt; use status::ResultExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use xatu::SourceFile; use std::process::ExitCode;
use xatu::{Report, SourceFile};
fn is_nix_file(path: &Path) -> bool { fn is_nix_file(path: &Path) -> bool {
path.extension().is_some_and(|extension| extension == "nix") path.extension().is_some_and(|extension| extension == "nix")
@@ -28,11 +29,18 @@ fn is_excluded_dir(path: &Path) -> bool {
} }
fn collect_source_files(path: &Path) -> status::Result<Vec<SourceFile>> { fn collect_source_files(path: &Path) -> status::Result<Vec<SourceFile>> {
collect_source_files_with_exclusions(path, false)
}
fn collect_source_files_with_exclusions(
path: &Path,
exclude_current_dir: bool,
) -> status::Result<Vec<SourceFile>> {
let metadata = std::fs::symlink_metadata(path) let metadata = std::fs::symlink_metadata(path)
.with_context(|| format!("failed to read metadata for {}", path.display()))?; .with_context(|| format!("failed to read metadata for {}", path.display()))?;
if metadata.file_type().is_dir() { if metadata.file_type().is_dir() {
if is_excluded_dir(path) { if exclude_current_dir && is_excluded_dir(path) {
return Ok(Vec::new()); return Ok(Vec::new());
} }
@@ -43,7 +51,7 @@ fn collect_source_files(path: &Path) -> status::Result<Vec<SourceFile>> {
{ {
let entry = let entry =
entry.with_context(|| format!("failed to read entry in {}", path.display()))?; entry.with_context(|| format!("failed to read entry in {}", path.display()))?;
files.extend(collect_source_files(&entry.path())?); files.extend(collect_source_files_with_exclusions(&entry.path(), true)?);
} }
return Ok(files); return Ok(files);
@@ -66,9 +74,7 @@ struct Args {
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
} }
fn main() -> status::Result<()> { fn analyze(args: Args) -> status::Result<Report> {
let args = Args::parse();
let paths: Vec<PathBuf> = args let paths: Vec<PathBuf> = args
.paths .paths
.into_iter() .into_iter()
@@ -83,8 +89,22 @@ fn main() -> status::Result<()> {
.flatten() .flatten()
.collect(); .collect();
let report = xatu::future_sight(files)?; xatu::future_sight(files)
print!("{report}"); }
Ok(()) fn main() -> ExitCode {
match analyze(Args::parse()) {
Ok(report) if report.is_clean() => {
println!("No findings.");
ExitCode::SUCCESS
}
Ok(report) => {
print!("{report}");
ExitCode::from(1)
}
Err(error) => {
eprintln!("{error}");
ExitCode::from(2)
}
}
} }