Skip to content

Commit 91fe4fc

Browse files
committed
feat(cli): make url optional for login command (#10925)
Allow `coder login` to log into existing deployment if available. Output to indicate saved URL was used. Update help and error messages to indicate that `coder login` is available as a command.
1 parent d2a74cf commit 91fe4fc

File tree

5 files changed

+75
-7
lines changed

5 files changed

+75
-7
lines changed

cli/login.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,22 +136,29 @@ func (r *RootCmd) login() *clibase.Cmd {
136136
useTokenForSession bool
137137
)
138138
cmd := &clibase.Cmd{
139-
Use: "login <url>",
139+
Use: "login [<url>]",
140140
Short: "Authenticate with Coder deployment",
141141
Middleware: clibase.RequireRangeArgs(0, 1),
142142
Handler: func(inv *clibase.Invocation) error {
143143
ctx := inv.Context()
144144
rawURL := ""
145145
if len(inv.Args) == 0 {
146146
rawURL = r.clientURL.String()
147+
if rawURL != "" {
148+
_, _ = fmt.Fprintf(inv.Stdout, "Attempting to authenticate with environment URL: %s\n", rawURL)
149+
}
147150
} else {
148151
rawURL = inv.Args[0]
149152
}
150153

154+
if url, err := r.createConfig().URL().Read(); rawURL == "" && err == nil {
155+
_, _ = fmt.Fprintf(inv.Stdout, "Attempting to authenticate with config URL: %s\n", url)
156+
rawURL = url
157+
}
158+
151159
if rawURL == "" {
152160
return xerrors.Errorf("no url argument provided")
153161
}
154-
155162
if !strings.HasPrefix(rawURL, "http://") && !strings.HasPrefix(rawURL, "https://") {
156163
scheme := "https"
157164
if strings.HasPrefix(rawURL, "localhost") {

cli/login_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,62 @@ func TestLogin(t *testing.T) {
215215
<-doneChan
216216
})
217217

218+
t.Run("ExistingUserURLSavedInConfig", func(t *testing.T) {
219+
t.Parallel()
220+
client := coderdtest.New(t, nil)
221+
url := client.URL.String()
222+
coderdtest.CreateFirstUser(t, client)
223+
224+
inv, root := clitest.New(t, "login", "--no-open")
225+
clitest.SetupConfig(t, client, root)
226+
227+
doneChan := make(chan struct{})
228+
pty := ptytest.New(t).Attach(inv)
229+
go func() {
230+
defer close(doneChan)
231+
err := inv.Run()
232+
assert.NoError(t, err)
233+
}()
234+
235+
pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with config URL: %s", url))
236+
pty.ExpectMatch("Paste your token here:")
237+
pty.WriteLine(client.SessionToken())
238+
if runtime.GOOS != "windows" {
239+
// For some reason, the match does not show up on Windows.
240+
pty.ExpectMatch(client.SessionToken())
241+
}
242+
pty.ExpectMatch("Welcome to Coder")
243+
<-doneChan
244+
})
245+
246+
t.Run("ExistingUserURLSavedInEnv", func(t *testing.T) {
247+
t.Parallel()
248+
client := coderdtest.New(t, nil)
249+
url := client.URL.String()
250+
coderdtest.CreateFirstUser(t, client)
251+
252+
inv, _ := clitest.New(t, "login", "--no-open")
253+
inv.Environ.Set("CODER_URL", url)
254+
255+
doneChan := make(chan struct{})
256+
pty := ptytest.New(t).Attach(inv)
257+
go func() {
258+
defer close(doneChan)
259+
err := inv.Run()
260+
assert.NoError(t, err)
261+
}()
262+
263+
pty.ExpectMatch(fmt.Sprintf("Attempting to authenticate with environment URL: %s", url))
264+
pty.ExpectMatch("Paste your token here:")
265+
pty.WriteLine(client.SessionToken())
266+
if runtime.GOOS != "windows" {
267+
// For some reason, the match does not show up on Windows.
268+
pty.ExpectMatch(client.SessionToken())
269+
}
270+
pty.ExpectMatch("Welcome to Coder")
271+
<-doneChan
272+
})
273+
218274
t.Run("ExistingUserInvalidTokenTTY", func(t *testing.T) {
219275
t.Parallel()
220276
client := coderdtest.New(t, nil)

cli/logout_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func TestLogout(t *testing.T) {
119119
go func() {
120120
defer close(logoutChan)
121121
err = logout.Run()
122-
assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login <url>'.")
122+
assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login'.")
123123
}()
124124

125125
<-logoutChan

cli/root.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ const (
6565
varVerbose = "verbose"
6666
varOrganizationSelect = "organization"
6767
varDisableDirect = "disable-direct-connections"
68-
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
68+
69+
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
70+
notLoggedInURLSavedMessage = "You are not logged in. Try logging in using 'coder login'."
6971

7072
envNoVersionCheck = "CODER_NO_VERSION_WARNING"
7173
envNoFeatureWarning = "CODER_NO_FEATURE_WARNING"
@@ -77,7 +79,10 @@ const (
7779
envURL = "CODER_URL"
7880
)
7981

80-
var errUnauthenticated = xerrors.New(notLoggedInMessage)
82+
var (
83+
errUnauthenticated = xerrors.New(notLoggedInMessage)
84+
errUnauthenticatedURLSaved = xerrors.New(notLoggedInURLSavedMessage)
85+
)
8186

8287
func (r *RootCmd) Core() []*clibase.Cmd {
8388
// Please re-sort this list alphabetically if you change it!
@@ -574,7 +579,7 @@ func (r *RootCmd) initClientInternal(client *codersdk.Client, allowTokenMissing
574579
// If the configuration files are absent, the user is logged out
575580
if os.IsNotExist(err) {
576581
if !allowTokenMissing {
577-
return errUnauthenticated
582+
return errUnauthenticatedURLSaved
578583
}
579584
} else if err != nil {
580585
return err

cli/testdata/coder_login_--help.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
coder v0.0.0-devel
22

33
USAGE:
4-
coder login [flags] <url>
4+
coder login [flags] [<url>]
55

66
Authenticate with Coder deployment
77

0 commit comments

Comments
 (0)