Skip to content

Commit b743fba

Browse files
authored
chore: migrate api command to urfave/cli (#1301)
* chore: migrate api command to urfave/cli * run goimports
1 parent 653c378 commit b743fba

4 files changed

Lines changed: 82 additions & 53 deletions

File tree

cmd/src/api.go

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,19 @@ package main
33
import (
44
"context"
55
"encoding/json"
6-
"flag"
76
"fmt"
87
"io"
98
"os"
109
"strings"
1110

12-
"github.com/sourcegraph/src-cli/internal/api"
11+
"github.com/sourcegraph/src-cli/internal/clicompat"
1312
"github.com/sourcegraph/src-cli/internal/cmderrors"
1413

1514
"github.com/mattn/go-isatty"
15+
"github.com/urfave/cli/v3"
1616
)
1717

18-
func init() {
19-
usage := `
18+
const apiExamples = `
2019
Exit codes:
2120
2221
0: Success
@@ -43,28 +42,26 @@ Examples:
4342
$ src api -get-curl -query='query { currentUser { username } }'
4443
`
4544

46-
flagSet := flag.NewFlagSet("api", flag.ExitOnError)
47-
usageFunc := func() {
48-
fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src %s':\n", flagSet.Name())
49-
flagSet.PrintDefaults()
50-
fmt.Println(usage)
51-
}
52-
var (
53-
queryFlag = flagSet.String("query", "", "GraphQL query to execute, e.g. 'query { currentUser { username } }' (stdin otherwise)")
54-
varsFlag = flagSet.String("vars", "", `GraphQL query variables to include as JSON string, e.g. '{"var": "val", "var2": "val2"}'`)
55-
apiFlags = api.NewFlags(flagSet)
56-
)
57-
58-
handler := func(args []string) error {
59-
err := flagSet.Parse(args)
60-
if err != nil {
61-
return err
62-
}
63-
64-
// Build the GraphQL request.
65-
query := *queryFlag
45+
var apiCommand = clicompat.Wrap(&cli.Command{
46+
Name: "api",
47+
Usage: "interacts with the Sourcegraph GraphQL API",
48+
UsageText: "src api [options] [variable=value ...]",
49+
Description: apiExamples,
50+
HideVersion: true,
51+
DisableSliceFlagSeparator: true,
52+
Flags: clicompat.WithAPIFlags(
53+
&cli.StringFlag{
54+
Name: "query",
55+
Usage: "GraphQL query to execute, e.g. 'query { currentUser { username } }' (stdin otherwise)",
56+
},
57+
&cli.StringFlag{
58+
Name: "vars",
59+
Usage: `GraphQL query variables to include as JSON string, e.g. '{"var": "val", "var2": "val2"}'`,
60+
},
61+
),
62+
Action: func(ctx context.Context, cmd *cli.Command) error {
63+
query := cmd.String("query")
6664
if query == "" {
67-
// Read query from stdin instead.
6865
if isatty.IsTerminal(os.Stdin.Fd()) {
6966
return cmderrors.Usage("expected query to be piped into 'src api' or -query flag to be specified")
7067
}
@@ -75,42 +72,40 @@ Examples:
7572
query = string(data)
7673
}
7774

78-
// Determine which variables to use in the request.
7975
vars := map[string]any{}
80-
if *varsFlag != "" {
81-
if err := json.Unmarshal([]byte(*varsFlag), &vars); err != nil {
76+
if raw := cmd.String("vars"); raw != "" {
77+
if err := json.Unmarshal([]byte(raw), &vars); err != nil {
8278
return err
8379
}
8480
}
85-
for _, arg := range flagSet.Args() {
86-
idx := strings.Index(arg, "=")
87-
if idx == -1 {
81+
for _, arg := range cmd.Args().Slice() {
82+
key, value, ok := strings.Cut(arg, "=")
83+
if !ok {
8884
return cmderrors.Usagef("parsing argument %q expected 'variable=value' syntax (missing equals)", arg)
8985
}
90-
key := arg[:idx]
91-
value := arg[idx+1:]
9286
vars[key] = value
9387
}
9488

95-
// Perform the request.
96-
var result any
97-
if ok, err := cfg.apiClient(apiFlags, flagSet.Output()).NewRequest(query, vars).DoRaw(context.Background(), &result); err != nil || !ok {
89+
var result struct {
90+
Data any `json:"data,omitempty"`
91+
Errors []json.RawMessage `json:"errors,omitempty"`
92+
}
93+
client := cfg.apiClient(clicompat.APIFlagsFromCmd(cmd), cmd.Writer)
94+
if ok, err := client.NewRequest(query, vars).DoRaw(ctx, &result); err != nil || !ok {
9895
return err
9996
}
10097

101-
// Print the formatted JSON.
102-
f, err := marshalIndent(result)
98+
formatted, err := marshalIndent(result)
10399
if err != nil {
104100
return err
105101
}
106-
fmt.Println(string(f))
102+
_, err = fmt.Fprintln(cmd.Writer, string(formatted))
103+
if err != nil {
104+
return err
105+
}
106+
if len(result.Errors) > 0 {
107+
return cmderrors.ExitCode(cmderrors.GraphqlErrorsExitCode, nil)
108+
}
107109
return nil
108-
}
109-
110-
// Register the command.
111-
commands = append(commands, &command{
112-
flagSet: flagSet,
113-
handler: handler,
114-
usageFunc: usageFunc,
115-
})
116-
}
110+
},
111+
})

cmd/src/run_migration_compat.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
var migratedCommands = map[string]*cli.Command{
1919
"abc": abcCommand,
20+
"api": apiCommand,
2021
"auth": authCommand,
2122
"login": loginCommand,
2223
"version": versionCommand,
@@ -68,6 +69,12 @@ func runMigrated() (int, error) {
6869
if errors.HasType[*cmderrors.UsageError](err) {
6970
return 2, nil
7071
}
72+
if e, ok := err.(*cmderrors.ExitCodeError); ok {
73+
if e.HasError() {
74+
return e.Code(), e
75+
}
76+
return e.Code(), nil
77+
}
7178
var exitErr cli.ExitCoder
7279
if errors.AsInterface(err, &exitErr) {
7380
return exitErr.ExitCode(), err

internal/clicompat/errors.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package clicompat
33
import (
44
"context"
55
"fmt"
6-
"os"
76

87
"github.com/sourcegraph/src-cli/internal/cmderrors"
98
"github.com/urfave/cli/v3"
109
)
1110

1211
func OnUsageError(ctx context.Context, cmd *cli.Command, err error, isSubCommand bool) error {
13-
fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
14-
cli.DefaultPrintHelp(os.Stderr, cmd.CustomHelpTemplate, cmd)
12+
out := errWriter(cmd)
13+
fmt.Fprintf(out, "error: %s\n", err.Error())
14+
printCommandHelp(out, cmd)
1515
return cmderrors.Usage(err.Error())
1616
}

internal/clicompat/help.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package clicompat
33
import (
44
"context"
55
"fmt"
6+
"io"
7+
"os"
68

79
"github.com/sourcegraph/sourcegraph/lib/errors"
810
"github.com/sourcegraph/src-cli/internal/cmderrors"
@@ -30,9 +32,34 @@ func wrapWithHelpOnUsageError(action cli.ActionFunc) cli.ActionFunc {
3032
return func(ctx context.Context, cmd *cli.Command) error {
3133
err := action(ctx, cmd)
3234
if err != nil && errors.HasType[*cmderrors.UsageError](err) {
33-
_, _ = fmt.Fprintf(cmd.Root().ErrWriter, "error: %s\n---\n", err)
34-
cli.DefaultPrintHelp(cmd.Root().ErrWriter, cmd.CustomHelpTemplate, cmd)
35+
out := errWriter(cmd)
36+
_, _ = fmt.Fprintf(out, "error: %s\n---\n", err)
37+
printCommandHelp(out, cmd)
3538
}
3639
return err
3740
}
3841
}
42+
43+
func errWriter(cmd *cli.Command) io.Writer {
44+
if cmd == nil || cmd.Root() == nil || cmd.Root().ErrWriter == nil {
45+
return os.Stderr
46+
}
47+
return cmd.Root().ErrWriter
48+
}
49+
50+
func printCommandHelp(out io.Writer, cmd *cli.Command) {
51+
if cmd == nil {
52+
return
53+
}
54+
55+
tmpl := cmd.CustomHelpTemplate
56+
if tmpl == "" {
57+
if len(cmd.Commands) == 0 {
58+
tmpl = cli.CommandHelpTemplate
59+
} else {
60+
tmpl = cli.SubcommandHelpTemplate
61+
}
62+
}
63+
64+
cli.HelpPrinter(out, tmpl, cmd)
65+
}

0 commit comments

Comments
 (0)