Skip to content

Commit cc75989

Browse files
committed
chore/cli: migrate users to urfave/cli
The command migration is moving legacy commander commands onto urfave/cli so they share the same help, flag, and dispatch behavior as newly migrated commands. Migrate src users and its list, get, create, delete, prune, and tag subcommands to cli.Command definitions. Register both users and user in the migrated command map, and remove users from the legacy docs path so generated docs come from the migrated root command. Test Plan: - go test ./cmd/src - go test ./... - go run ./cmd/src users -h - go run ./cmd/src user -h - go run ./cmd/src users list -h
1 parent e1a9e41 commit cc75989

10 files changed

Lines changed: 275 additions & 260 deletions

File tree

cmd/src/doc.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ Examples:
6363
"extsvc": &extsvcCommands,
6464
"code-intel": &codeintelCommands,
6565
"repos": &reposCommands,
66-
"users": &usersCommands,
6766
}
6867

6968
pending := out.Pending(output.Line("", output.StylePending, "Rendering Markdown..."))

cmd/src/run_migration_compat.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ var migratedCommands = map[string]*cli.Command{
2525
// instead of writing lots of plumbing to handle an alias, lets just register it explicitly for now
2626
"codeowner": codeownersCommand,
2727
"login": loginCommand,
28-
"orgs": orgsCommand,
28+
"orgs": orgsCommand,
2929
"org": orgsCommand,
30+
"users": usersCommand,
31+
"user": usersCommand,
3032
"version": versionCommand,
3133
}
3234

cmd/src/users.go

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
package main
22

33
import (
4-
"flag"
5-
"fmt"
4+
"github.com/sourcegraph/src-cli/internal/clicompat"
5+
"github.com/urfave/cli/v3"
66
)
77

8-
var usersCommands commander
8+
var usersCommand = clicompat.Wrap(&cli.Command{
9+
Name: "users",
10+
Aliases: []string{"user"},
11+
Usage: "manages users",
12+
UsageText: "src users [command options]",
13+
Description: usersExamples,
14+
HideVersion: true,
15+
Commands: []*cli.Command{
16+
usersListCommand,
17+
usersGetCommand,
18+
usersCreateCommand,
19+
usersDeleteCommand,
20+
usersPruneCommand,
21+
usersTagCommand,
22+
},
23+
})
924

10-
func init() {
11-
usage := `'src users' is a tool that manages users on a Sourcegraph instance.
25+
const usersExamples = `'src users' is a tool that manages users on a Sourcegraph instance.
1226
1327
Usage:
1428
@@ -26,23 +40,6 @@ The commands are:
2640
Use "src users [command] -h" for more information about a command.
2741
`
2842

29-
flagSet := flag.NewFlagSet("users", flag.ExitOnError)
30-
handler := func(args []string) error {
31-
usersCommands.run(flagSet, "src users", usage, args)
32-
return nil
33-
}
34-
35-
// Register the command.
36-
commands = append(commands, &command{
37-
flagSet: flagSet,
38-
aliases: []string{"user"},
39-
handler: handler,
40-
usageFunc: func() {
41-
fmt.Println(usage)
42-
},
43-
})
44-
}
45-
4643
const userFragment = `
4744
fragment UserFields on User {
4845
id

cmd/src/users_create.go

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ package main
22

33
import (
44
"context"
5-
"flag"
65
"fmt"
76

8-
"github.com/sourcegraph/src-cli/internal/api"
7+
"github.com/sourcegraph/src-cli/internal/clicompat"
8+
"github.com/urfave/cli/v3"
99
)
1010

11-
func init() {
12-
usage := `
11+
const usersCreateExamples = `
1312
Examples:
1413
1514
Create a user account:
@@ -18,25 +17,36 @@ Examples:
1817
1918
`
2019

21-
flagSet := flag.NewFlagSet("create", flag.ExitOnError)
22-
usageFunc := func() {
23-
fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src users %s':\n", flagSet.Name())
24-
flagSet.PrintDefaults()
25-
fmt.Println(usage)
26-
}
27-
var (
28-
usernameFlag = flagSet.String("username", "", `The new user's username. (required)`)
29-
emailFlag = flagSet.String("email", "", `The new user's email address. (required)`)
30-
resetPasswordURLFlag = flagSet.Bool("reset-password-url", false, `Print the reset password URL to manually send to the new user.`)
31-
apiFlags = api.NewFlags(flagSet)
32-
)
20+
var usersCreateCommand = clicompat.Wrap(&cli.Command{
21+
Name: "create",
22+
Usage: "creates a user account",
23+
UsageText: "src users create [options]",
24+
Description: usersCreateExamples,
25+
HideVersion: true,
26+
Flags: clicompat.WithAPIFlags(
27+
&cli.StringFlag{
28+
Name: "username",
29+
Usage: "The new user's username.",
30+
Required: true,
31+
Validator: requiresNotEmpty("provide a username name using -username"),
32+
},
33+
&cli.StringFlag{
34+
Name: "email",
35+
Usage: "The new user's email address",
36+
Required: true,
37+
Validator: requiresNotEmpty("provide a email name using -email"),
38+
},
39+
&cli.BoolFlag{
40+
Name: "reset-password-url",
41+
Usage: "Print the reset password URL to manually send to the new user.",
42+
},
43+
),
44+
Action: func(ctx context.Context, cmd *cli.Command) error {
45+
username := cmd.String("username")
46+
email := cmd.String("email")
47+
resetPasswordURL := cmd.Bool("reset-password-url")
3348

34-
handler := func(args []string) error {
35-
if err := flagSet.Parse(args); err != nil {
36-
return err
37-
}
38-
39-
client := cfg.apiClient(apiFlags, flagSet.Output())
49+
client := cfg.apiClient(clicompat.APIFlagsFromCmd(cmd), cmd.Writer)
4050

4151
query := `mutation CreateUser(
4252
$username: String!,
@@ -56,24 +66,22 @@ Examples:
5666
}
5767
}
5868
if ok, err := client.NewRequest(query, map[string]any{
59-
"username": *usernameFlag,
60-
"email": *emailFlag,
61-
}).Do(context.Background(), &result); err != nil || !ok {
69+
"username": username,
70+
"email": email,
71+
}).Do(ctx, &result); err != nil || !ok {
6272
return err
6373
}
6474

65-
fmt.Printf("User %q created.\n", *usernameFlag)
66-
if *resetPasswordURLFlag && result.CreateUser.ResetPasswordURL != "" {
67-
fmt.Println()
68-
fmt.Printf("\tReset pasword URL: %s\n", result.CreateUser.ResetPasswordURL)
75+
if _, err := fmt.Fprintf(cmd.Writer, "User %q created.\n", username); err != nil {
76+
return err
77+
}
78+
if resetPasswordURL && result.CreateUser.ResetPasswordURL != "" {
79+
if _, err := fmt.Fprintln(cmd.Writer); err != nil {
80+
return err
81+
}
82+
_, err := fmt.Fprintf(cmd.Writer, "\tReset pasword URL: %s\n", result.CreateUser.ResetPasswordURL)
83+
return err
6984
}
7085
return nil
71-
}
72-
73-
// Register the command.
74-
usersCommands = append(usersCommands, &command{
75-
flagSet: flagSet,
76-
handler: handler,
77-
usageFunc: usageFunc,
78-
})
79-
}
86+
},
87+
})

cmd/src/users_delete.go

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@ package main
33
import (
44
"bufio"
55
"context"
6-
"flag"
76
"fmt"
87
"os"
98
"strconv"
109
"strings"
1110

12-
"github.com/sourcegraph/src-cli/internal/api"
11+
"github.com/sourcegraph/src-cli/internal/clicompat"
12+
"github.com/urfave/cli/v3"
1313
)
1414

15-
func init() {
16-
usage := `
15+
const usersDeleteExamples = `
1716
Examples:
1817
1918
Delete a user account by ID:
@@ -30,38 +29,38 @@ Examples:
3029
3130
`
3231

33-
flagSet := flag.NewFlagSet("delete", flag.ExitOnError)
34-
usageFunc := func() {
35-
fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src users %s':\n", flagSet.Name())
36-
flagSet.PrintDefaults()
37-
fmt.Println(usage)
38-
}
39-
var (
40-
userIDFlag = flagSet.String("id", "", `The ID of the user to delete.`)
41-
apiFlags = api.NewFlags(flagSet)
42-
)
43-
44-
handler := func(args []string) error {
45-
if err := flagSet.Parse(args); err != nil {
46-
return err
47-
}
48-
49-
client := cfg.apiClient(apiFlags, flagSet.Output())
50-
51-
if *userIDFlag == "" {
32+
var usersDeleteCommand = clicompat.Wrap(&cli.Command{
33+
Name: "delete",
34+
Usage: "deletes a user account",
35+
UsageText: "src users delete [options]",
36+
Description: usersDeleteExamples,
37+
HideVersion: true,
38+
Flags: clicompat.WithAPIFlags(
39+
&cli.StringFlag{
40+
Name: "id",
41+
Usage: "The ID of the user to delete.",
42+
},
43+
),
44+
Action: func(ctx context.Context, cmd *cli.Command) error {
45+
userID := cmd.String("id")
46+
client := cfg.apiClient(clicompat.APIFlagsFromCmd(cmd), cmd.Writer)
47+
48+
if userID == "" {
5249
query := `query UsersTotalCountCountUsers { users { totalCount } }`
5350

5451
var result struct {
5552
Users struct {
5653
TotalCount int
5754
}
5855
}
59-
ok, err := client.NewQuery(query).Do(context.Background(), &result)
56+
ok, err := client.NewQuery(query).Do(ctx, &result)
6057
if err != nil || !ok {
6158
return err
6259
}
6360

64-
fmt.Printf("No user ID specified. This would delete %d users.\nType in this number to confirm and hit return: ", result.Users.TotalCount)
61+
if _, err := fmt.Fprintf(cmd.Writer, "No user ID specified. This would delete %d users.\nType in this number to confirm and hit return: ", result.Users.TotalCount); err != nil {
62+
return err
63+
}
6564
reader := bufio.NewReader(os.Stdin)
6665
text, err := reader.ReadString('\n')
6766
if err != nil {
@@ -74,8 +73,8 @@ Examples:
7473
}
7574

7675
if count != result.Users.TotalCount {
77-
fmt.Println("Number does not match. Aborting.")
78-
return nil
76+
_, err := fmt.Fprintln(cmd.Writer, "Number does not match. Aborting.")
77+
return err
7978
}
8079
}
8180

@@ -93,19 +92,12 @@ Examples:
9392
DeleteUser struct{}
9493
}
9594
if ok, err := client.NewRequest(query, map[string]any{
96-
"user": *userIDFlag,
97-
}).Do(context.Background(), &result); err != nil || !ok {
95+
"user": userID,
96+
}).Do(ctx, &result); err != nil || !ok {
9897
return err
9998
}
10099

101-
fmt.Printf("User with ID %q deleted.\n", *userIDFlag)
102-
return nil
103-
}
104-
105-
// Register the command.
106-
usersCommands = append(usersCommands, &command{
107-
flagSet: flagSet,
108-
handler: handler,
109-
usageFunc: usageFunc,
110-
})
111-
}
100+
_, err := fmt.Fprintf(cmd.Writer, "User with ID %q deleted.\n", userID)
101+
return err
102+
},
103+
})

0 commit comments

Comments
 (0)