Commit 7664da8384
7664da8384df3367b3a29d26296e91ebd6c4ff05
parent: 4eb0f95ce5
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-28 07:29 UTC
auth --help: force --path= for a bare CLI-only alias group
gitbay auth --help resolved cliPath and the registered prefix to the
same string ("auth"), so withCLIPath's equality shortcut sent no
--path= and the server, seeing no CLIPath, printed the registered rows
a caller cannot type. The registry has no "auth" command at all, so
the shortcut's premise (matching paths mean no override needed) does
not hold for it.
helpArgv forces --path= for the CLI-only alias groups (auth) regardless
of the equality check; every other group, whose name is a real
registered prefix, is unaffected.
Ref #267
Layout: unified · split
cmd/gitbay/main.go
+23 −1
| @@ -390,6 +390,28 @@ func group(use, short string, subs ...*cobra.Command) *cobra.Command { |
| 390 | return c |
390 | return c |
| 391 | } |
391 | } |
| 392 | |
392 | |
| |
393 | // aliasGroupNames are CLI-only noun names with no matching registry |
| |
394 | // prefix (internal/control's nounAliases keys — kept in sync by hand, |
| |
395 | // since the CLI has no registry to consult): auth's own cliPath is |
| |
396 | // "auth", equal to the prefix it asks help for, so withCLIPath's |
| |
397 | // equality shortcut reads that as "no override needed" even though no |
| |
398 | // registered command is named "auth" at all, and help would silently |
| |
399 | // fall back to the registered forms (#267 review finding). Force |
| |
400 | // --path= for these regardless of the equality check. |
| |
401 | var aliasGroupNames = map[string]bool{"auth": true} |
| |
402 | |
| |
403 | // helpArgv builds the server argv for a group's --help: --path=cliPath |
| |
404 | // when the CLI's path differs from the registered prefix it names, or |
| |
405 | // unconditionally when prefix is a CLI-only alias grouping the registry |
| |
406 | // never answers to under that name. |
| |
407 | func helpArgv(prefix, cliPath string) []string { |
| |
408 | argv := []string{"help", prefix} |
| |
409 | if aliasGroupNames[prefix] { |
| |
410 | return append([]string{"--path=" + cliPath}, argv...) |
| |
411 | } |
| |
412 | return withCLIPath(cliPath, prefix, argv) |
| |
413 | } |
| |
414 | |
| 393 | // serverHelp prints the registry's usage for a prefix and reports whether |
415 | // serverHelp prints the registry's usage for a prefix and reports whether |
| 394 | // it did. cliPath is the group's own path in the CLI. At a terminal it |
416 | // it did. cliPath is the group's own path in the CLI. At a terminal it |
| 395 | // goes through the terminal-aware path, so it gets the same |
417 | // goes through the terminal-aware path, so it gets the same |
| @@ -401,7 +423,7 @@ func serverHelp(prefix, cliPath string) bool { |
| 401 | if err != nil { |
423 | if err != nil { |
| 402 | return false |
424 | return false |
| 403 | } |
425 | } |
| 404 | argv := withCLIPath(cliPath, prefix, []string{"help", prefix}) |
426 | argv := helpArgv(prefix, cliPath) |
| 405 | if term.IsTerminal(int(os.Stdout.Fd())) { |
427 | if term.IsTerminal(int(os.Stdout.Fd())) { |
| 406 | return runSSH(t, argv, strings.NewReader("")) == 0 |
428 | return runSSH(t, argv, strings.NewReader("")) == 0 |
| 407 | } |
429 | } |
cmd/gitbay/serverpath_test.go
+36
| @@ -43,3 +43,39 @@ func TestServerPathMismatches(t *testing.T) { |
| 43 | t.Errorf("mismatched CLI paths = %v\nwant %v", got, want) |
43 | t.Errorf("mismatched CLI paths = %v\nwant %v", got, want) |
| 44 | } |
44 | } |
| 45 | } |
45 | } |
| |
46 | |
| |
47 | // TestHelpArgvSendsThePathForAnAliasGroup pins the review-round fix: a |
| |
48 | // bare `gitbay auth --help` (cliPath == prefix == "auth") must still send |
| |
49 | // --path=auth, since the registry has no "auth" command for withCLIPath's |
| |
50 | // equality shortcut to correctly skip (#267 follow-up). |
| |
51 | func TestHelpArgvSendsThePathForAnAliasGroup(t *testing.T) { |
| |
52 | got := helpArgv("auth", "auth") |
| |
53 | want := []string{"--path=auth", "help", "auth"} |
| |
54 | if !slices.Equal(got, want) { |
| |
55 | t.Errorf("helpArgv(auth, auth) = %v, want %v", got, want) |
| |
56 | } |
| |
57 | } |
| |
58 | |
| |
59 | // TestHelpArgvSendsNoPathWhenCLIMatchesTheRegistry pins the unaffected |
| |
60 | // case: an ordinary noun's cliPath and registered prefix are the same |
| |
61 | // string, and it is not a CLI-only alias grouping, so no --path= is sent |
| |
62 | // and the server resolves it on its own. |
| |
63 | func TestHelpArgvSendsNoPathWhenCLIMatchesTheRegistry(t *testing.T) { |
| |
64 | got := helpArgv("issue", "issue") |
| |
65 | want := []string{"help", "issue"} |
| |
66 | if !slices.Equal(got, want) { |
| |
67 | t.Errorf("helpArgv(issue, issue) = %v, want %v", got, want) |
| |
68 | } |
| |
69 | } |
| |
70 | |
| |
71 | // TestHelpArgvSendsThePathForAMismatchedGroup pins the ordinary |
| |
72 | // Task 2.1 case, unchanged by the alias-group fix: a nested group whose |
| |
73 | // cliPath differs from its registered prefix (auth keys, for keys) |
| |
74 | // already sends --path= through withCLIPath. |
| |
75 | func TestHelpArgvSendsThePathForAMismatchedGroup(t *testing.T) { |
| |
76 | got := helpArgv("keys", "auth keys") |
| |
77 | want := []string{"--path=auth keys", "help", "keys"} |
| |
78 | if !slices.Equal(got, want) { |
| |
79 | t.Errorf("helpArgv(keys, auth keys) = %v, want %v", got, want) |
| |
80 | } |
| |
81 | } |
e2e/cliusage_test.go
+28
| @@ -52,6 +52,34 @@ func TestCLIUsagePrintsTheInvokingPath(t *testing.T) { |
| 52 | if strings.Contains(errOut, "auth") { |
52 | if strings.Contains(errOut, "auth") { |
| 53 | t.Errorf("stock ssh saw the CLI's auth grouping: %q", errOut) |
53 | t.Errorf("stock ssh saw the CLI's auth grouping: %q", errOut) |
| 54 | } |
54 | } |
| |
55 | |
| |
56 | // A bare `gitbay auth --help` (cliPath == "auth", the registered |
| |
57 | // prefix it asks for) must still send --path=auth: the registry has |
| |
58 | // no "auth" command, so withCLIPath's equality shortcut would |
| |
59 | // otherwise read that as "no override needed" and print the |
| |
60 | // registered rows the CLI cannot actually type (#267 review finding). |
| |
61 | out, errOut, code = c.run(t, "", "", "auth", "--help") |
| |
62 | if code != 0 { |
| |
63 | t.Errorf("gitbay auth --help: exit %d, stdout %q, stderr %q", code, out, errOut) |
| |
64 | } |
| |
65 | for _, want := range []string{"auth keys add", "auth export"} { |
| |
66 | if !strings.Contains(out, want) { |
| |
67 | t.Errorf("gitbay auth --help: missing %q in %q", want, out) |
| |
68 | } |
| |
69 | } |
| |
70 | |
| |
71 | out, errOut, code = inst.ssh(t, key, "", "help", "auth") |
| |
72 | if code != 0 { |
| |
73 | t.Errorf("ssh help auth: exit %d, stdout %q, stderr %q", code, out, errOut) |
| |
74 | } |
| |
75 | for _, want := range []string{"keys add", "account export"} { |
| |
76 | if !strings.Contains(out, want) { |
| |
77 | t.Errorf("ssh help auth: missing %q in %q", want, out) |
| |
78 | } |
| |
79 | } |
| |
80 | if strings.Contains(out, "auth keys add") { |
| |
81 | t.Errorf("stock ssh saw the CLI's auth grouping: %q", out) |
| |
82 | } |
| 55 | } |
83 | } |
| 56 | |
84 | |
| 57 | // keys add and pgp add wire stdin directly to the server rather than going |
85 | // keys add and pgp add wire stdin directly to the server rather than going |