From e413ad06e4f7c66ef7709190222c3494e575a0c0 Mon Sep 17 00:00:00 2001 From: William Bezuidenhout Date: Mon, 20 Apr 2026 10:52:39 +0200 Subject: [PATCH 1/2] migrate login to urfave/cli --- cmd/src/login.go | 60 ++++++++++++++------------------- cmd/src/login_test.go | 32 ++++++++++++++++++ cmd/src/run_migration_compat.go | 1 + 3 files changed, 58 insertions(+), 35 deletions(-) diff --git a/cmd/src/login.go b/cmd/src/login.go index 889994af51..d2ee0dc138 100644 --- a/cmd/src/login.go +++ b/cmd/src/login.go @@ -2,19 +2,18 @@ package main import ( "context" - "flag" "fmt" "io" "net/url" - "os" "github.com/sourcegraph/src-cli/internal/api" + "github.com/sourcegraph/src-cli/internal/clicompat" "github.com/sourcegraph/src-cli/internal/cmderrors" "github.com/sourcegraph/src-cli/internal/oauth" + "github.com/urfave/cli/v3" ) -func init() { - usage := `'src login' helps you authenticate 'src' to access a Sourcegraph instance with your user credentials. +const loginExamples = `'src login' helps you authenticate 'src' to access a Sourcegraph instance with your user credentials. Usage: @@ -35,29 +34,18 @@ Examples: $ src login https://sourcegraph.com ` - flagSet := flag.NewFlagSet("login", flag.ExitOnError) - usageFunc := func() { - fmt.Fprintln(flag.CommandLine.Output(), usage) - flagSet.PrintDefaults() - } - - var ( - apiFlags = api.NewFlags(flagSet) - ) - - handler := func(args []string) error { - if err := flagSet.Parse(args); err != nil { - return err - } - - if cfg.configFilePath != "" { - fmt.Fprintln(os.Stderr) - fmt.Fprintf(os.Stderr, "⚠️ Warning: Configuring src with a JSON file is deprecated. Please migrate to using the env vars SRC_ENDPOINT, SRC_ACCESS_TOKEN, and SRC_PROXY instead, and then remove %s. See https://github.com/sourcegraph/src-cli#readme for more information.\n", cfg.configFilePath) - } - - if flagSet.NArg() >= 1 { - arg := flagSet.Arg(0) - loginEndpointURL, err := parseEndpoint(arg) +var loginCommand = clicompat.Wrap(&cli.Command{ + Name: "login", + Usage: "authenticate to a Sourcegraph instance with your user credentials", + UsageText: "src login [command options] [SOURCEGRAPH_URL]", + Description: loginExamples, + HideVersion: true, + Flags: clicompat.WithAPIFlags(), + Action: func(ctx context.Context, cmd *cli.Command) error { + var loginEndpointURL *url.URL + if cmd.Args().Present() { + arg := cmd.Args().First() + u, err := parseEndpoint(arg) if err != nil { return cmderrors.Usage(fmt.Sprintf("invalid endpoint URL: %s", arg)) } @@ -79,6 +67,7 @@ Examples: cfg.endpointURL = loginEndpointURL } + apiFlags := clicompat.APIFlagsFromCmd(cmd) client := cfg.apiClient(apiFlags, io.Discard) return loginCmd(context.Background(), loginParams{ @@ -87,15 +76,16 @@ Examples: out: os.Stdout, apiFlags: apiFlags, oauthClient: oauth.NewClient(oauth.DefaultClientID), + return loginCmd(context.Background(), loginParams{ + cfg: cfg, + client: client, + out: os.Stdout, + apiFlags: apiFlags, + oauthClient: oauth.NewClient(oauth.DefaultClientID), + loginEndpointURL: loginEndpointURL, }) - } - - commands = append(commands, &command{ - flagSet: flagSet, - handler: handler, - usageFunc: usageFunc, - }) -} + }, +}) type loginParams struct { cfg *config diff --git a/cmd/src/login_test.go b/cmd/src/login_test.go index 5dba8b464b..4da0a5a63b 100644 --- a/cmd/src/login_test.go +++ b/cmd/src/login_test.go @@ -14,6 +14,7 @@ import ( "github.com/sourcegraph/src-cli/internal/cmderrors" "github.com/sourcegraph/src-cli/internal/oauth" + "github.com/urfave/cli/v3" ) func mustParseURL(t *testing.T, raw string) *url.URL { @@ -139,6 +140,37 @@ func TestLogin(t *testing.T) { }) } +func TestLoginCommand(t *testing.T) { + prevCfg := cfg + defer func() { cfg = prevCfg }() + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, `{"data":{"currentUser":{"username":"alice"}}}`) + })) + defer s.Close() + + cfg = &config{ + endpointURL: mustParseURL(t, s.URL), + accessToken: "x", + } + + var out bytes.Buffer + cmd := *loginCommand + cmd.Writer = &out + cmd.ErrWriter = &out + cmd.ExitErrHandler = func(context.Context, *cli.Command, error) {} + + err := cmd.Run(context.Background(), []string{"login", s.URL}) + if err != nil { + t.Fatal(err) + } + + want := "\n✔︎ Authenticated as alice on " + s.URL + "\n\n\n💡 Tip: To use this endpoint in your shell, run:\n\n export SRC_ENDPOINT=" + s.URL + "\n\n" + if out.String() != want { + t.Fatalf("output = %q, want %q", out.String(), want) + } +} + type fakeOAuthClient struct { startErr error startCalled *bool diff --git a/cmd/src/run_migration_compat.go b/cmd/src/run_migration_compat.go index fde21bd5ee..eada89896c 100644 --- a/cmd/src/run_migration_compat.go +++ b/cmd/src/run_migration_compat.go @@ -18,6 +18,7 @@ import ( var migratedCommands = map[string]*cli.Command{ "abc": abcCommand, "auth": authCommand, + "login": loginCommand, "version": versionCommand, } From c911f7da1b9043ebfc3b60cadd3a73e2365317cd Mon Sep 17 00:00:00 2001 From: William Bezuidenhout Date: Tue, 21 Apr 2026 12:06:42 +0200 Subject: [PATCH 2/2] fix merge issues --- cmd/src/login.go | 16 +++++++--------- cmd/src/login_test.go | 32 -------------------------------- 2 files changed, 7 insertions(+), 41 deletions(-) diff --git a/cmd/src/login.go b/cmd/src/login.go index d2ee0dc138..b5e1862b5f 100644 --- a/cmd/src/login.go +++ b/cmd/src/login.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/url" + "os" "github.com/sourcegraph/src-cli/internal/api" "github.com/sourcegraph/src-cli/internal/clicompat" @@ -42,10 +43,14 @@ var loginCommand = clicompat.Wrap(&cli.Command{ HideVersion: true, Flags: clicompat.WithAPIFlags(), Action: func(ctx context.Context, cmd *cli.Command) error { - var loginEndpointURL *url.URL + if cfg.configFilePath != "" { + fmt.Fprintln(os.Stderr) + fmt.Fprintf(os.Stderr, "⚠️ Warning: Configuring src with a JSON file is deprecated. Please migrate to using the env vars SRC_ENDPOINT, SRC_ACCESS_TOKEN, and SRC_PROXY instead, and then remove %s. See https://github.com/sourcegraph/src-cli#readme for more information.\n", cfg.configFilePath) + } + if cmd.Args().Present() { arg := cmd.Args().First() - u, err := parseEndpoint(arg) + loginEndpointURL, err := parseEndpoint(arg) if err != nil { return cmderrors.Usage(fmt.Sprintf("invalid endpoint URL: %s", arg)) } @@ -76,13 +81,6 @@ var loginCommand = clicompat.Wrap(&cli.Command{ out: os.Stdout, apiFlags: apiFlags, oauthClient: oauth.NewClient(oauth.DefaultClientID), - return loginCmd(context.Background(), loginParams{ - cfg: cfg, - client: client, - out: os.Stdout, - apiFlags: apiFlags, - oauthClient: oauth.NewClient(oauth.DefaultClientID), - loginEndpointURL: loginEndpointURL, }) }, }) diff --git a/cmd/src/login_test.go b/cmd/src/login_test.go index 4da0a5a63b..5dba8b464b 100644 --- a/cmd/src/login_test.go +++ b/cmd/src/login_test.go @@ -14,7 +14,6 @@ import ( "github.com/sourcegraph/src-cli/internal/cmderrors" "github.com/sourcegraph/src-cli/internal/oauth" - "github.com/urfave/cli/v3" ) func mustParseURL(t *testing.T, raw string) *url.URL { @@ -140,37 +139,6 @@ func TestLogin(t *testing.T) { }) } -func TestLoginCommand(t *testing.T) { - prevCfg := cfg - defer func() { cfg = prevCfg }() - - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, `{"data":{"currentUser":{"username":"alice"}}}`) - })) - defer s.Close() - - cfg = &config{ - endpointURL: mustParseURL(t, s.URL), - accessToken: "x", - } - - var out bytes.Buffer - cmd := *loginCommand - cmd.Writer = &out - cmd.ErrWriter = &out - cmd.ExitErrHandler = func(context.Context, *cli.Command, error) {} - - err := cmd.Run(context.Background(), []string{"login", s.URL}) - if err != nil { - t.Fatal(err) - } - - want := "\n✔︎ Authenticated as alice on " + s.URL + "\n\n\n💡 Tip: To use this endpoint in your shell, run:\n\n export SRC_ENDPOINT=" + s.URL + "\n\n" - if out.String() != want { - t.Fatalf("output = %q, want %q", out.String(), want) - } -} - type fakeOAuthClient struct { startErr error startCalled *bool