diff --git a/cli/login.go b/cli/login.go index 17cb206e1e..422496052e 100644 --- a/cli/login.go +++ b/cli/login.go @@ -136,16 +136,28 @@ func (r *RootCmd) login() *clibase.Cmd { useTokenForSession bool ) cmd := &clibase.Cmd{ - Use: "login ", + Use: "login []", Short: "Authenticate with Coder deployment", Middleware: clibase.RequireRangeArgs(0, 1), Handler: func(inv *clibase.Invocation) error { ctx := inv.Context() rawURL := "" + var urlSource string + if len(inv.Args) == 0 { rawURL = r.clientURL.String() + urlSource = "flag" + if rawURL != "" && rawURL == inv.Environ.Get(envURL) { + urlSource = "environment" + } } else { rawURL = inv.Args[0] + urlSource = "argument" + } + + if url, err := r.createConfig().URL().Read(); rawURL == "" && err == nil { + urlSource = "config" + rawURL = url } if rawURL == "" { @@ -187,6 +199,9 @@ func (r *RootCmd) login() *clibase.Cmd { if err != nil { return xerrors.Errorf("Failed to check server %q for first user, is the URL correct and is coder accessible from your browser? Error - has initial user: %w", serverURL.String(), err) } + + _, _ = fmt.Fprintf(inv.Stdout, "Attempting to authenticate with %s URL: '%s'\n", urlSource, serverURL) + if !hasFirstUser { _, _ = fmt.Fprintf(inv.Stdout, Caret+"Your Coder deployment hasn't been set up!\n") diff --git a/cli/login_test.go b/cli/login_test.go index 1fb6576c3e..3cf9dc1945 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -116,6 +116,7 @@ func TestLogin(t *testing.T) { clitest.Start(t, inv) + pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with flag URL: '%s'", client.URL.String())) matches := []string{ "first user?", "yes", "username", "testuser", @@ -205,6 +206,7 @@ func TestLogin(t *testing.T) { assert.NoError(t, err) }() + pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with argument URL: '%s'", client.URL.String())) pty.ExpectMatch("Paste your token here:") pty.WriteLine(client.SessionToken()) if runtime.GOOS != "windows" { @@ -215,6 +217,52 @@ func TestLogin(t *testing.T) { <-doneChan }) + t.Run("ExistingUserURLSavedInConfig", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + url := client.URL.String() + coderdtest.CreateFirstUser(t, client) + + inv, root := clitest.New(t, "login", "--no-open") + clitest.SetupConfig(t, client, root) + + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with config URL: '%s'", url)) + pty.ExpectMatch("Paste your token here:") + pty.WriteLine(client.SessionToken()) + <-doneChan + }) + + t.Run("ExistingUserURLSavedInEnv", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + url := client.URL.String() + coderdtest.CreateFirstUser(t, client) + + inv, _ := clitest.New(t, "login", "--no-open") + inv.Environ.Set("CODER_URL", url) + + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with environment URL: '%s'", url)) + pty.ExpectMatch("Paste your token here:") + pty.WriteLine(client.SessionToken()) + <-doneChan + }) + t.Run("ExistingUserInvalidTokenTTY", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) diff --git a/cli/logout_test.go b/cli/logout_test.go index b7c1a571a6..493ccb5dd2 100644 --- a/cli/logout_test.go +++ b/cli/logout_test.go @@ -119,7 +119,7 @@ func TestLogout(t *testing.T) { go func() { defer close(logoutChan) err = logout.Run() - assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login '.") + assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login'.") }() <-logoutChan diff --git a/cli/root.go b/cli/root.go index b4a381052c..4c6d5f213d 100644 --- a/cli/root.go +++ b/cli/root.go @@ -65,7 +65,9 @@ const ( varVerbose = "verbose" varOrganizationSelect = "organization" varDisableDirect = "disable-direct-connections" - notLoggedInMessage = "You are not logged in. Try logging in using 'coder login '." + + notLoggedInMessage = "You are not logged in. Try logging in using 'coder login '." + notLoggedInURLSavedMessage = "You are not logged in. Try logging in using 'coder login'." envNoVersionCheck = "CODER_NO_VERSION_WARNING" envNoFeatureWarning = "CODER_NO_FEATURE_WARNING" @@ -77,7 +79,10 @@ const ( envURL = "CODER_URL" ) -var errUnauthenticated = xerrors.New(notLoggedInMessage) +var ( + errUnauthenticated = xerrors.New(notLoggedInMessage) + errUnauthenticatedURLSaved = xerrors.New(notLoggedInURLSavedMessage) +) func (r *RootCmd) Core() []*clibase.Cmd { // Please re-sort this list alphabetically if you change it! @@ -574,7 +579,7 @@ func (r *RootCmd) initClientInternal(client *codersdk.Client, allowTokenMissing // If the configuration files are absent, the user is logged out if os.IsNotExist(err) { if !allowTokenMissing { - return errUnauthenticated + return errUnauthenticatedURLSaved } } else if err != nil { return err diff --git a/cli/testdata/coder_login_--help.golden b/cli/testdata/coder_login_--help.golden index 7e0b8ce324..f6fe15dc07 100644 --- a/cli/testdata/coder_login_--help.golden +++ b/cli/testdata/coder_login_--help.golden @@ -1,7 +1,7 @@ coder v0.0.0-devel USAGE: - coder login [flags] + coder login [flags] [] Authenticate with Coder deployment diff --git a/docs/cli/login.md b/docs/cli/login.md index f7604d42db..0cc972421b 100644 --- a/docs/cli/login.md +++ b/docs/cli/login.md @@ -7,7 +7,7 @@ Authenticate with Coder deployment ## Usage ```console -coder login [flags] +coder login [flags] [] ``` ## Options