Commit 1c22065e25

1c22065e2524cfed36a3af744500a34adef75b88

parent: 1aaf5bca7b

Verified · cmc ci/build: success ci/test: success ci/vuln: success

cmc <hello@cleberg.net> · 2026-09-03 00:10 UTC

cli: help forwards to the server for a prefix or --json

Cobra's built-in help owned `gitbay help`, so `gitbay help --json`
failed on an unknown flag and `gitbay help mr` printed cobra's one-line
summaries while the wiki documented the server's reference.

`gitbay help` bare is still the local tree. With a prefix or --json it
runs the server's help, which is the only place flags are written down
and whose JSON is the contract. The coverage test attaches the help
command the way Execute does, so `help` is wired like any other path.

Closes #86

Layout: unified · split

cmd/gitbay/coverage_test.go +3 −2
@@ -13,7 +13,6 @@ import (
1313// registry must be reachable by typing it, or the CLI is not the
1414// complete interface the design claims.
1515var notInCLI = map[string]string{
16 "help": "cobra owns `gitbay help`; the registry's is reached by `<cmd> --help`",
1716 "runner next": "the CI runner's wire protocol, not for humans",
1817 "runner done": "the CI runner's wire protocol, not for humans",
1918 "runner log": "the CI runner's wire protocol, not for humans",
@@ -36,7 +35,9 @@ func TestEveryCommandIsReachable(t *testing.T) {
3635 walk(sub)
3736 }
3837 }
39 walk(newRoot())
38 root := newRoot()
39 root.InitDefaultHelpCmd() // attaches the help command the way Execute does
40 walk(root)
4041
4142 for _, cmd := range control.Commands() {
4243 path := strings.Join(cmd.Path, " ")
cmd/gitbay/main.go +27
@@ -32,6 +32,7 @@ func newRoot() *cobra.Command {
3232 SilenceUsage: true,
3333 SilenceErrors: true,
3434 }
35 root.SetHelpCommand(helpCmd(root))
3536
3637 root.AddCommand(
3738 authCmd(),
@@ -576,3 +577,29 @@ func manCmd(root *cobra.Command) *cobra.Command {
576577 cmd.Flags().StringVar(&dir, "dir", "man", "output directory")
577578 return cmd
578579}
580
581// helpCmd is `gitbay help`. Bare, it is cobra's tree of local commands.
582// With anything after it — a prefix such as `mr`, or --json — it is the
583// server's help: the registry is the only place flags are written down,
584// and its JSON is the contract. Cobra's built-in help used to swallow
585// both forms, so `gitbay help --json` failed on an unknown flag.
586func helpCmd(root *cobra.Command) *cobra.Command {
587 return &cobra.Command{
588 Use: "help [<prefix>...] [--json]",
589 Short: "this list, or the server's command reference for a prefix",
590 Annotations: map[string]string{serverPath: "help", stdinMode: "none"},
591 DisableFlagParsing: true,
592 RunE: func(cmd *cobra.Command, args []string) error {
593 if len(args) == 0 {
594 return root.Help()
595 }
596 t, err := resolveTarget()
597 if err != nil {
598 fmt.Fprintln(os.Stderr, "gitbay:", err)
599 os.Exit(protocol.ExitFailure)
600 }
601 os.Exit(runSSH(t, append([]string{"help"}, args...), strings.NewReader("")))
602 return nil
603 },
604 }
605}