Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/tool/exec: Correctly parse and join cmd args #3238

Closed
infogulch opened this issue Jun 19, 2024 · 2 comments
Closed

pkg/tool/exec: Correctly parse and join cmd args #3238

infogulch opened this issue Jun 19, 2024 · 2 comments
Assignees
Labels
FeatureRequest New feature or request

Comments

@infogulch
Copy link

infogulch commented Jun 19, 2024

I'm frustrated that the string form of tool/exec.Run.cmd does not parse args correctly, and that when it prints a command after it exits with an error status it incorrectly joins the args so it is not possible to simply copy the printed command and run it manually. The string form is nice to use for the simple case of running a single command, but it fails to correctly parse the args even if they are appropriately quoted. Also it fails to correctly print out debug information about a command that failed, as it uses plain concatenation instead of properly quoting arguments with spaces or special characters, even if they were passed as separate args in the list form.

For example, consider the following tool file:

package example
import "tool/exec"
command: parse: run: exec.Run & {cmd: "bash -c 'echo hello world'"}
command: join: run: exec.Run & {cmd: ["bash", "-c", "echo hello world; exit 1"]}
$ cue cmd parse
hello: -c: line 1: unexpected EOF while looking for matching `''
hello: -c: line 2: syntax error: unexpected end of file
task failed: command "bash -c 'echo hello world'" failed: exit status 2

$ cue cmd join
hello world
task failed: command "bash -c echo hello world; exit 1" failed: exit status 1

The parse command fails because tool/exec incorrectly parses the final quoted arg as a multiple args. The join command fails intentionally, but the printed error is incorrect, since the string bash -c echo hello world; exit 1 is not the same command as what cue cmd ran.


I'd like tool/exec to correctly parse args in the string form and correctly join args in both forms for debug printing. This can be implemented by importing the venerate github.com/kballard/go-shellquote library and updating the code to use it for splitting / joining instead of strings.Fields / plain concatenation. I have implemented this approach in PR #3207 which also includes new tests for these cases and more.

Some alternatives:

  • Instead of importing the library it may be better to copy the relevant 250 LOC directly into the repo instead of adding a dependency. This is a tradeoff, but I have no opinion either way.
  • Arg parsing/joining may be useful to cue tool authors directly, so we could expose the splitting/joining methods from the library as new tools (say tool/exec.ParseArgs/JoinArgs), and refactor the Run tool to perform the parsing/joining via the new tools within cue instead.
@infogulch infogulch added FeatureRequest New feature or request Triage Requires triage/attention labels Jun 19, 2024
@mvdan
Copy link
Member

mvdan commented Jul 5, 2024

Also it fails to correctly print out debug information about a command that failed, as it uses plain concatenation instead of properly quoting arguments with spaces or special characters, even if they were passed as separate args in the list form.

That is indeed a bug no matter what; if the input command was exec.Run & {cmd: ["bash", "-c", "echo hello world; exit 1"]}, we should not print it as an error like task failed: command "bash -c echo hello world; exit 1" failed, as that is indeed ambiguous. We could print the list of strings in either CUE or Go syntax, both of which can handle quoting.

The string form is nice to use for the simple case of running a single command, but it fails to correctly parse the args even if they are appropriately quoted.

Just so I understand, why do you want to use the string form when the list form exists? Quoting my reply from #3207 (comment) to join the threads:

I'm sure you have seen, but much like Go's os/exec, we support command arguments as a list of strings, which sidesteps this issue entirely. The majority of users, particularly those needing multiple arguments or arguments involving spaces, should use a list of strings rather than a single string that needs to be split up. This is easier for users to understand and maintain, and also doesn't require anyone to deal with shell syntax, which is unnecessary complexity. Particularly as it requires adding an extra dependency to CUE :)

I hope that makes sense. I would advocate for deprecating the string form of the command because perhaps it's a footgun that it uses https://pkg.go.dev/strings#Fields - it is rather basic. And perhaps some users would expect that it would use shell syntax, and we don't want to give that impression.

It seems to me like deprecating the string form in favor of [...string], or at least documenting that it's only meant for very simple use cases which don't involve whitespace in arguments, is a simpler and better way forward. I do not think we should make string split and unquote fields like a shell because:

  • It is a significant amount of added complexity for little benefit. Such quoting exists in shells where the input is just characters, but here in CUE, we have a structured form for the arguments, [...string], with CUE syntax.
  • Using CUE syntax is more consistent. ["foo", "bar baz"] reads better and is far more intuitive to CUE readers than "foo 'bar baz'" with nested quoting mixing different syntaxes.
  • Shell quoting is a loose standard. Even if we document that we only support POSIX Shell quoting, e.g. no $' from Bash, the popularity of shells like Bash or Zsh may cause confusion and frustration for users down the line.

@mvdan mvdan self-assigned this Jul 9, 2024
@mvdan mvdan removed the Triage Requires triage/attention label Jul 9, 2024
cueckoo pushed a commit that referenced this issue Jul 9, 2024
The command is expected to fail, as we run the "false" program
which always fails and ignores any arguments.

The printed error is ambiguous, as it's not possible to see
what the original list of arguments was, hence the TODO.
The following commit will resolve this bug.

For #3238.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: Ie277772ce58dfac48df9440e0a53fd9f1fc309cc
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1197451
Reviewed-by: Roger Peppe <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
cueckoo pushed a commit that referenced this issue Jul 9, 2024
If we render the command arguments as a quoted Go string joining
the arguments via spaces, it's always going to be confusing if any
of those arguments contain any spaces.

We always use a slice now, even when the arguments don't contain
any spaces or even when there's just one argument,
but consistency seems more important than saving a few characters.

An alternative to Go slices with %q quoting, following Go syntax,
would be to quote a list of strings in CUE syntax.
However, we use Go syntax for lists and string quoting for errors
rather consistently, so it seems best to continue doing so here.

While here, add a godoc for mkCommand, avoid doing a lookup on the
"cmd" field twice, and simplify the code slightly.

Updates #3238.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: I1a2ae4e0c07236db566ddc9217c1722bb5e99da4
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1197452
Reviewed-by: Roger Peppe <[email protected]>
Unity-Result: CUE porcuepine <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
@cueckoo cueckoo closed this as completed in 7e1f140 Jul 9, 2024
@mvdan
Copy link
Member

mvdan commented Jul 9, 2024

For the time being, I've marked this issue as resolved by making the error messages non-ambiguous, and clearly documenting how the string form works with splitting arguments and its limitation. If this is still confusing to users, we can look into more agressive measures such as deprecating or even removing the string form in favor of the list form, with a rewrite in cue fix for existing users. For the time being, that seems premature, as you're the first and only user to run into this issue so far.

Let me know if that makes sense, or if you'd like to reopen this issue to discuss anything else. I'll close the PR for the time being as well, because like I said, we don't intend to add shell quoting support to the tool/exec package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FeatureRequest New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants