Skip to content

fix(err,test): preserve argument quoting and exit codes in rtk err/test#3002

Open
albatrossflyon-coder wants to merge 1 commit into
rtk-ai:developfrom
albatrossflyon-coder:fix/err-test-quote-preservation
Open

fix(err,test): preserve argument quoting and exit codes in rtk err/test#3002
albatrossflyon-coder wants to merge 1 commit into
rtk-ai:developfrom
albatrossflyon-coder:fix/err-test-quote-preservation

Conversation

@albatrossflyon-coder

Copy link
Copy Markdown

Summary

Fixes #2985.

rtk err <cmd> and rtk test <cmd> build their sh -c/cmd /C command string with argv.join(" "). The OS already splits argv into correct elements by the time this process sees them, but the naive join discards that boundary information. An argument containing shell metacharacters — node -e 'console.log("a")', python3 -c "print(...)" — gets flattened into the join, and the shell re-splits it on those same characters when re-parsing the string.

On the exit-code half: I don't think exit-code propagation itself is actually broken — run_streaming correctly reads and returns the child's real exit status (verified this is the same code path I already fixed for #2994 in #2997). The exit-0 symptom in the issue's repro is what happens when there's no child failure to propagate: the shell's own syntax-error diagnostic looks like a command failure, but the target command was never actually invoked in a form the shell could execute — there's nothing non-zero to report because nothing that ran actually failed. Fixing the quoting fixes this too, verified below.

Fix

Added quote_shell_arg/shell_quote_join to re-quote each argument before joining, so the shell reconstructs the original argument boundaries instead of re-splitting a flattened string.

Windows needed one extra step beyond quoting. std::process::Command's own argument-escaping was re-escaping the already-quoted command string a second time when spawning cmd.exe — corrupting it before cmd.exe ever saw it. Using raw_arg (passes the string through untouched, no extra escaping layer) for the Windows /C invocation fixes that. Without it I could reproduce a different broken-quoting failure than the one reported — same root cause, different platform-specific escaping bug.

Verified live

$ rtk err node -e 'console.log("a")'
[ok] Command completed successfully (no errors)
$ echo $?
0

A genuinely failing command with quotes, semicolons, and parens all in one argument:

$ rtk err node -e 'console.log("before"); process.exit(3)'
[FAIL] Command failed (exit code: 3)
  before
$ echo $?
3

Also confirmed python -c "print('hello world')" and that legitimate shell features (a plain rtk err invocation without special characters) are unaffected.

Testing

  • New unit tests: quote_shell_arg leaves safe args unquoted / quotes args with metacharacters / quotes args with spaces; shell_quote_join preserves argument boundaries (the exact regression scenario) / handles empty argv
  • cargo test: 2449 passed, 8 ignored, 0 failed
  • cargo clippy --all-targets -- -D warnings: clean

Note

I tested this on Windows (not the macOS environment in the original report), so I can't directly confirm the POSIX sh -c branch against a real POSIX shell — quote_shell_arg's non-Windows path uses the standard single-quote-wrap-with-'\''-escape technique, which is the textbook-correct approach for this, but flagging that a maintainer with a Unix box handy might want to double check it directly.

`rtk err <cmd>` and `rtk test <cmd>` built their sh -c/cmd /C command
string with `argv.join(" ")`. The OS already split argv into correct
elements, but the naive join throws that boundary information away --
an argument containing shell metacharacters (`node -e 'console.log("a")'`,
`python3 -c "print(...)"`) gets flattened, then the shell re-splits it
on those same characters and fails, most commonly with a syntax error.

On top of the quoting loss, a failure like this was being reported as
exit 0 -- not because exit-code propagation itself was broken (it
correctly reads the child's real status), but because there was no
child failure to correctly propagate: the shell's own diagnostic
about the malformed command was easy to mistake for the target
command failing, when actually the target command was never run
at all in a form the shell could execute successfully.

Add quote_shell_arg/shell_quote_join to re-quote each argument before
joining, so the shell reconstructs the exact original boundaries.
Windows needed one extra step beyond quoting: std::process::Command's
own arg-escaping was re-escaping the already-quoted command string a
second time when spawning cmd.exe, corrupting it. Using raw_arg (which
passes the string through untouched) for the Windows /C invocation
fixes that -- verified live with quotes, spaces, semicolons, and
parens in one argument, both for a passing command and one that exits
non-zero after printing partial output.

Fixes rtk-ai#2985

@tapheret2 tapheret2 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: fix(err,test): preserve argument quoting and exit codes in rtk err/test

Files: src/cmds/rust/runner.rs, src/main.rs
Size: +118 / -20

Notes

  • Static pass on the patch: no obvious blockers from the diff alone.

Thanks @albatrossflyon-coder — independent review on the patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rtk err loses argument quoting (breaks node -e / python3 -c) and exits 0 on failure

2 participants