Explorar el Código

Show a command's error over ssh instead of exiting 1 in silence

When Run returned an error, ssh exited 1 with nothing written and the
error went only to the debug log. The control socket already carried the
error to its client; ssh now writes it to the session as well.

- execLine writes a Run error to the error stream before returning it
- print-cert -raw reports "Unable to marshal the certificate" as a refusal, as master did

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Matt Richardson hace 1 día
padre
commit
de85615651
Se han modificado 3 ficheros con 22 adiciones y 1 borrados
  1. 1 1
      commands.go
  2. 3 0
      commands/command.go
  3. 18 0
      commands/registry_test.go

+ 1 - 1
commands.go

@@ -527,7 +527,7 @@ func runPrintCert(ifce *Interface, args *printCertFlags, a []string) (commands.R
 	if args.Raw {
 		b, err := c.MarshalPEM()
 		if err != nil {
-			return commands.Result{}, err
+			return commands.Failed("Unable to marshal the certificate: %s", err), nil
 		}
 		return commands.Value(certPEM{PEM: string(b)}), nil
 	}

+ 3 - 0
commands/command.go

@@ -165,8 +165,11 @@ func (r *Registry) execLine(c *Command, args []string, w Output) error {
 
 	format := presentationFormat(fl)
 
+	// An error from Run is something the command could not do rather than a refusal it
+	// chose, but the person at the prompt still hears it; the surface logs it too.
 	res, err := c.Run(fs, operands)
 	if err != nil {
+		_, _ = fmt.Fprintln(w.Err, err.Error())
 		return err
 	}
 

+ 18 - 0
commands/registry_test.go

@@ -439,3 +439,21 @@ func TestParameterMistakesAreParamErrors(t *testing.T) {
 	require.EqualError(t, err, "ran and failed")
 	assert.NotErrorAs(t, err, &pe, "a Run error is the command's, not the caller's")
 }
+
+// TestRunErrorIsShown: an error Run returns reaches the person at the prompt on the
+// error stream, as well as the surface that logs it.
+func TestRunErrorIsShown(t *testing.T) {
+	r := New()
+	r.Register(&Command{
+		Name:             "broken",
+		ShortDescription: "cannot do what it is asked",
+		Run: func(fs any, a []string) (Result, error) {
+			return Result{}, errors.New("no certificate is loaded")
+		},
+	})
+
+	out, errOut := &bytes.Buffer{}, &bytes.Buffer{}
+	require.EqualError(t, r.DispatchArgs([]string{"broken"}, Output{Out: out, Err: errOut}), "no certificate is loaded")
+	assert.Empty(t, out.String())
+	assert.Equal(t, "no certificate is loaded\n", errOut.String())
+}