| 77 | } |
| 78 | |
| 79 | func logoutRun(opts *LogoutOptions) error { |
| 80 | hostname := opts.Hostname |
| 81 | username := opts.Username |
| 82 | |
| 83 | cfg, err := opts.Config() |
| 84 | if err != nil { |
| 85 | return err |
| 86 | } |
| 87 | authCfg := cfg.Authentication() |
| 88 | |
| 89 | knownHosts := authCfg.Hosts() |
| 90 | if len(knownHosts) == 0 { |
| 91 | return fmt.Errorf("not logged in to any hosts") |
| 92 | } |
| 93 | |
| 94 | if hostname != "" { |
| 95 | if !slices.Contains(knownHosts, hostname) { |
| 96 | return fmt.Errorf("not logged in to %s", hostname) |
| 97 | } |
| 98 | |
| 99 | if username != "" { |
| 100 | knownUsers := cfg.Authentication().UsersForHost(hostname) |
| 101 | if !slices.Contains(knownUsers, username) { |
| 102 | return fmt.Errorf("not logged in to %s account %s", hostname, username) |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | type hostUser struct { |
| 108 | host string |
| 109 | user string |
| 110 | } |
| 111 | var candidates []hostUser |
| 112 | |
| 113 | for _, host := range knownHosts { |
| 114 | if hostname != "" && host != hostname { |
| 115 | continue |
| 116 | } |
| 117 | knownUsers := cfg.Authentication().UsersForHost(host) |
| 118 | for _, user := range knownUsers { |
| 119 | if username != "" && user != username { |
| 120 | continue |
| 121 | } |
| 122 | candidates = append(candidates, hostUser{host: host, user: user}) |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | if len(candidates) == 0 { |
| 127 | return errors.New("no accounts matched that criteria") |
| 128 | } else if len(candidates) == 1 { |
| 129 | hostname = candidates[0].host |
| 130 | username = candidates[0].user |
| 131 | } else if !opts.IO.CanPrompt() { |
| 132 | return errors.New("unable to determine which account to log out of, please specify `--hostname` and `--user`") |
| 133 | } else { |
| 134 | prompts := make([]string, len(candidates)) |
| 135 | for i, c := range candidates { |
| 136 | prompts[i] = fmt.Sprintf("%s (%s)", c.user, c.host) |