fix(pi-natives/shell): ensured OK output for successful empty minimization
- Added `with_text` to keep minimized output byte counters consistent when replacing output text. - Wrapped successful pipeline and overlay minimizer results with a post-step that emits `OK\n` when the transform empties output on success. - Added tests to confirm successful empty-output minimization becomes `OK\n` and failures keep empty output.
This commit is contained in:
@@ -97,6 +97,15 @@ impl MinimizerOutput {
|
||||
self
|
||||
}
|
||||
|
||||
/// Replace the transformed text while keeping minimization telemetry
|
||||
/// coherent.
|
||||
#[must_use]
|
||||
pub fn with_text(mut self, text: String) -> Self {
|
||||
self.output_bytes = text.len();
|
||||
self.text = text;
|
||||
self
|
||||
}
|
||||
|
||||
/// Byte count saved by this filter (0 for passthrough).
|
||||
#[allow(dead_code, reason = "test-only API surface")]
|
||||
pub const fn bytes_saved(&self) -> usize {
|
||||
|
||||
@@ -127,7 +127,7 @@ fn apply_identity(
|
||||
};
|
||||
let label = program_label(&identity.program);
|
||||
let overlaid = apply_pipeline_overlay(config, &identity.program, rust_output, label);
|
||||
return overlaid.with_original(captured);
|
||||
return ensure_success_visible(overlaid, exit_code).with_original(captured);
|
||||
}
|
||||
|
||||
if let Some(pipeline) = resolve_pipeline(config, &identity.program, subcommand) {
|
||||
@@ -139,15 +139,25 @@ fn apply_identity(
|
||||
if text == captured {
|
||||
return MinimizerOutput::passthrough(captured).labeled("pipeline-noop");
|
||||
}
|
||||
return MinimizerOutput::transformed(text, captured.len())
|
||||
.labeled("pipeline")
|
||||
.with_original(captured);
|
||||
return ensure_success_visible(
|
||||
MinimizerOutput::transformed(text, captured.len()).labeled("pipeline"),
|
||||
exit_code,
|
||||
)
|
||||
.with_original(captured);
|
||||
}
|
||||
|
||||
record_unknown_command(command);
|
||||
MinimizerOutput::passthrough(captured).labeled("unsupported")
|
||||
}
|
||||
|
||||
fn ensure_success_visible(output: MinimizerOutput, exit_code: i32) -> MinimizerOutput {
|
||||
if exit_code == 0 && output.changed && output.text.trim().is_empty() {
|
||||
output.with_text("OK\n".to_string())
|
||||
} else {
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-program label for telemetry. Returns one of a fixed static set so the
|
||||
/// N-API boundary can carry it as `&'static str` without allocation.
|
||||
fn program_label(program: &str) -> &'static str {
|
||||
@@ -321,6 +331,33 @@ mod tests {
|
||||
assert!(out.text.contains("modified: 1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_minimization_keeps_visible_ok_when_filter_removes_all_lines() {
|
||||
let cfg = MinimizerConfig { enabled: true, ..Default::default() };
|
||||
let out = apply(
|
||||
"cargo build",
|
||||
" Compiling app v0.1.0\n Finished `dev` profile [unoptimized + debuginfo] target(s) \
|
||||
in 1.23s\n",
|
||||
0,
|
||||
&cfg,
|
||||
);
|
||||
|
||||
assert!(out.changed);
|
||||
assert_eq!(out.text, "OK\n");
|
||||
assert_eq!(out.output_bytes, out.text.len());
|
||||
assert!(out.original_text.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_minimization_does_not_invent_ok_for_empty_output() {
|
||||
let cfg = MinimizerConfig { enabled: true, ..Default::default() };
|
||||
let out = apply("cargo build", " Compiling app v0.1.0\n", 1, &cfg);
|
||||
|
||||
assert!(out.changed);
|
||||
assert_eq!(out.text, "");
|
||||
assert!(out.original_text.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_command_is_passthrough() {
|
||||
let cfg = MinimizerConfig { enabled: true, ..Default::default() };
|
||||
|
||||
@@ -106,11 +106,7 @@ function packageTaskName(packageName: string | undefined, packageDir: string, sc
|
||||
return `${packageName ?? packageDir}/${scriptName}`;
|
||||
}
|
||||
|
||||
function tasksForPackage(options: {
|
||||
pkg: PackageJsonInfo;
|
||||
packageDir: string;
|
||||
namespaced: boolean;
|
||||
}): RunnerTask[] {
|
||||
function tasksForPackage(options: { pkg: PackageJsonInfo; packageDir: string; namespaced: boolean }): RunnerTask[] {
|
||||
return options.pkg.scripts.map(scriptName => ({
|
||||
name: options.namespaced ? packageTaskName(options.pkg.name, options.packageDir, scriptName) : scriptName,
|
||||
doc: options.namespaced ? options.packageDir : undefined,
|
||||
|
||||
@@ -152,9 +152,7 @@ describe("recipe", () => {
|
||||
"crate-b/bin/worker",
|
||||
]);
|
||||
expect(
|
||||
resolveCommand("cargo:crate-a/example/demo", [
|
||||
{ id: "cargo", label: "Cargo", commandPrefix: "cargo", tasks },
|
||||
]),
|
||||
resolveCommand("cargo:crate-a/example/demo", [{ id: "cargo", label: "Cargo", commandPrefix: "cargo", tasks }]),
|
||||
).toEqual({ command: "cargo run --package 'crate-a' --example 'demo'" });
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Fixed
|
||||
|
||||
- Fixed shell minimizer output so successful commands whose noise is fully stripped still return `OK` instead of an artifact-only result
|
||||
|
||||
## [14.5.6] - 2026-04-29
|
||||
|
||||
|
||||
Reference in New Issue
Block a user