diff --git a/cmd/src/auth.go b/cmd/src/auth.go index eff5f4bbec..5c843ad700 100644 --- a/cmd/src/auth.go +++ b/cmd/src/auth.go @@ -1,37 +1,38 @@ package main import ( - "flag" - "fmt" -) + "context" -var authCommands commander + "github.com/sourcegraph/src-cli/internal/clicompat" + "github.com/urfave/cli/v3" +) -func init() { - usage := `'src auth' provides authentication-related helper commands. +const authExamples = ` +Authentication-related helper commands. -Usage: +Examples: - src auth command [command options] +Print the active auth token: -The commands are: +$ src auth token +sgp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - token prints the current authentication token or Authorization header +Print the current Authorization header: -Use "src auth [command] -h" for more information about a command. +$ src auth token --header +Authorization: token sgp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ` - flagSet := flag.NewFlagSet("auth", flag.ExitOnError) - handler := func(args []string) error { - authCommands.run(flagSet, "src auth", usage, args) - return nil - } - - commands = append(commands, &command{ - flagSet: flagSet, - handler: handler, - usageFunc: func() { - fmt.Println(usage) - }, - }) -} +var authCommand = clicompat.Wrap(&cli.Command{ + Name: "auth", + Usage: "authentication helper commands", + UsageText: "src auth [command options]", + Description: authExamples, + HideVersion: true, + Commands: []*cli.Command{ + authTokenCommand, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + return cli.ShowSubcommandHelp(cmd) + }, +}) diff --git a/cmd/src/auth_token.go b/cmd/src/auth_token.go index fdd6d03a42..9ae9c75330 100644 --- a/cmd/src/auth_token.go +++ b/cmd/src/auth_token.go @@ -2,12 +2,13 @@ package main import ( "context" - "flag" "fmt" "github.com/sourcegraph/sourcegraph/lib/errors" + "github.com/sourcegraph/src-cli/internal/clicompat" "github.com/sourcegraph/src-cli/internal/oauth" + "github.com/urfave/cli/v3" ) var ( @@ -21,37 +22,52 @@ type oauthTokenRefresher interface { GetToken(ctx context.Context) (oauth.Token, error) } -func init() { - flagSet := flag.NewFlagSet("token", flag.ExitOnError) - header := flagSet.Bool("header", false, "print the token as an Authorization header") - usageFunc := func() { - fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src auth token':\n\n") - fmt.Fprintf(flag.CommandLine.Output(), "Print the current authentication token.\n") - fmt.Fprintf(flag.CommandLine.Output(), "Use --header to print a complete Authorization header instead.\n\n") - flagSet.PrintDefaults() - } +const authTokenExamples = ` +Print the current authentication token. - handler := func(args []string) error { - if err := flagSet.Parse(args); err != nil { - return err - } +Use --header to print a complete Authorization header instead. + +Examples: + +Raw token output: + +$ src auth token +sgp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +Authorization header output: - token, err := resolveAuthToken(context.Background(), cfg) +$ src auth token --header +Authorization: token sgp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +If you are authenticated with OAuth instead of SRC_ACCESS_TOKEN, the header uses the Bearer scheme: + +$ src auth token --header +Authorization: Bearer eyJhbGciOi... +` + +var authTokenCommand = clicompat.Wrap(&cli.Command{ + Name: "token", + Usage: "prints the current authentication token or Authorization header", + UsageText: "src auth token [options]", + Description: authTokenExamples, + HideVersion: true, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "header", + Usage: "print the token as an Authorization header", + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + token, err := resolveAuthToken(ctx, cfg) if err != nil { return err } - token = formatAuthTokenOutput(token, cfg.AuthMode(), *header) - fmt.Println(token) - return nil - } - - authCommands = append(authCommands, &command{ - flagSet: flagSet, - handler: handler, - usageFunc: usageFunc, - }) -} + token = formatAuthTokenOutput(token, cfg.AuthMode(), cmd.Bool("header")) + _, err = fmt.Fprintln(cmd.Writer, token) + return err + }, +}) func resolveAuthToken(ctx context.Context, cfg *config) (string, error) { if err := cfg.requireCIAccessToken(); err != nil { diff --git a/cmd/src/auth_token_test.go b/cmd/src/auth_token_test.go index c351d06d75..1a5bfbcbaa 100644 --- a/cmd/src/auth_token_test.go +++ b/cmd/src/auth_token_test.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "context" "fmt" "net/url" @@ -125,6 +126,32 @@ func TestResolveAuthToken(t *testing.T) { }) } +func TestAuthTokenCommand(t *testing.T) { + reset := stubAuthTokenDependencies(t) + defer reset() + + prevCfg := cfg + defer func() { cfg = prevCfg }() + + cfg = &config{ + accessToken: "access-token", + endpointURL: mustParseURL(t, "https://example.com"), + } + + var out bytes.Buffer + cmd := *authTokenCommand + cmd.Writer = &out + cmd.ErrWriter = &out + + err := cmd.Run(context.Background(), []string{"token", "--header"}) + if err != nil { + t.Fatal(err) + } + if out.String() != "Authorization: token access-token\n" { + t.Fatalf("output = %q, want %q", out.String(), "Authorization: token access-token\n") + } +} + func TestFormatAuthTokenOutput(t *testing.T) { tests := []struct { name string diff --git a/cmd/src/run_migration_compat.go b/cmd/src/run_migration_compat.go index 36d4fae9f0..fde21bd5ee 100644 --- a/cmd/src/run_migration_compat.go +++ b/cmd/src/run_migration_compat.go @@ -17,6 +17,7 @@ import ( var migratedCommands = map[string]*cli.Command{ "abc": abcCommand, + "auth": authCommand, "version": versionCommand, }