Display message when logging into existing account

This commit is contained in:
William Martin 2023-11-28 13:25:45 +01:00
parent a9acece7dd
commit 1bf6023164
6 changed files with 101 additions and 1 deletions

View file

@ -387,6 +387,33 @@ func TestLogoutIgnoresErrorsFromConfigAndKeyring(t *testing.T) {
require.NoError(t, err)
}
func TestUsersForHostNoHost(t *testing.T) {
// Given we have a config with no hosts
authCfg := newTestAuthConfig(t)
// When we get the users for a host that doesn't exist
_, err := authCfg.UsersForHost("github.com")
// Then it returns an error
require.EqualError(t, err, "unknown host: github.com")
}
func TestUsersForHostWithUsers(t *testing.T) {
// Given we have a config with a host and users
authCfg := newTestAuthConfig(t)
_, err := authCfg.Login("github.com", "test-user-1", "test-token", "ssh", false)
require.NoError(t, err)
_, err = authCfg.Login("github.com", "test-user-2", "test-token", "ssh", false)
require.NoError(t, err)
// When we get the users for that host
users, err := authCfg.UsersForHost("github.com")
// Then it succeeds and returns the users
require.NoError(t, err)
require.Equal(t, []string{"test-user-1", "test-user-2"}, users)
}
func requireKeyWithValue(t *testing.T, cfg *ghConfig.Config, keys []string, value string) {
t.Helper()

View file

@ -373,6 +373,15 @@ func (c *AuthConfig) Logout(hostname, username string) error {
return ghConfig.Write(c.cfg)
}
func (c *AuthConfig) UsersForHost(hostname string) ([]string, error) {
users, err := c.cfg.Keys([]string{hostsKey, hostname, usersKey})
if err != nil {
return nil, fmt.Errorf("unknown host: %s", hostname)
}
return users, nil
}
func keyringServiceName(hostname string) string {
return "gh:" + hostname
}

View file

@ -177,6 +177,7 @@ func loginRun(opts *LoginOptions) error {
if err != nil {
return fmt.Errorf("error retrieving current user: %w", err)
}
// Adding a user key ensures that a nonempty host section gets written to the config file.
_, loginErr := authCfg.Login(hostname, username, opts.Token, opts.GitProtocol, !opts.InsecureStorage)
return loginErr

View file

@ -403,7 +403,7 @@ func Test_loginRun_nontty(t *testing.T) {
wantSecureToken: "abc123",
},
{
name: "given we are already logged in, a new user is added to the config",
name: "given we are already logged in, and log in as a new user, it is added to the config",
opts: &LoginOptions{
Hostname: "github.com",
Token: "newUserToken",
@ -677,6 +677,50 @@ func Test_loginRun_Survey(t *testing.T) {
wantErrOut: regexp.MustCompile("Logged in as jillv"),
wantSecureToken: "def456",
},
{
name: "given we log in as a user that is already in the config, we get an informational message",
opts: &LoginOptions{
Hostname: "github.com",
Interactive: true,
InsecureStorage: true,
},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.SelectFunc = func(prompt, _ string, opts []string) (int, error) {
switch prompt {
case "What is your preferred protocol for Git operations?":
return prompter.IndexFor(opts, "HTTPS")
case "How would you like to authenticate GitHub CLI?":
return prompter.IndexFor(opts, "Paste an authentication token")
}
return -1, prompter.NoSuchPromptErr(prompt)
}
},
cfgStubs: func(c *config.ConfigMock) {
_, err := c.Authentication().Login("github.com", "monalisa", "abc123", "https", false)
require.NoError(t, err)
},
runStubs: func(rs *run.CommandStubber) {
rs.Register(`git config credential\.https:/`, 1, "")
rs.Register(`git config credential\.helper`, 1, "")
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", ""), httpmock.ScopesResponder("repo,read:org"))
reg.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data":{"viewer":{"login":"monalisa"}}}`))
},
wantHosts: heredoc.Doc(`
github.com:
users:
monalisa:
oauth_token: def456
git_protocol: https
git_protocol: https
user: monalisa
oauth_token: def456
`),
wantErrOut: regexp.MustCompile(`! You were already logged in to this account`),
},
}
for _, tt := range tests {

View file

@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"os"
"slices"
"strings"
"github.com/MakeNowJust/heredoc"
@ -23,6 +24,7 @@ const defaultSSHKeyTitle = "GitHub CLI"
type iconfig interface {
Login(string, string, string, string, bool) (bool, error)
UsersForHost(string) ([]string, error)
}
type LoginOptions struct {
@ -183,6 +185,15 @@ func Login(opts *LoginOptions) error {
}
}
// Get these users before adding the new one, so that we can
// check whether the user was already logged in later.
//
// In this case we ignore the error if the host doesn't exist
// because that can occur when the user is logging into a host
// for the first time.
usersForHost, _ := cfg.UsersForHost(hostname)
userWasAlreadyLoggedIn := slices.Contains(usersForHost, username)
if gitProtocol != "" {
fmt.Fprintf(opts.IO.ErrOut, "- gh config set -h %s git_protocol %s\n", hostname, gitProtocol)
fmt.Fprintf(opts.IO.ErrOut, "%s Configured git protocol\n", cs.SuccessIcon())
@ -217,6 +228,10 @@ func Login(opts *LoginOptions) error {
}
fmt.Fprintf(opts.IO.ErrOut, "%s Logged in as %s\n", cs.SuccessIcon(), cs.Bold(username))
if userWasAlreadyLoggedIn {
fmt.Fprintf(opts.IO.ErrOut, "%s You were already logged in to this account\n", cs.WarningIcon())
}
return nil
}

View file

@ -25,6 +25,10 @@ func (c tinyConfig) Login(host, username, token, gitProtocol string, encrypt boo
return false, nil
}
func (c tinyConfig) UsersForHost(hostname string) ([]string, error) {
return nil, nil
}
func TestLogin_ssh(t *testing.T) {
dir := t.TempDir()
ios, _, stdout, stderr := iostreams.Test()