github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/auth/logout/logout.go (about) 1 package logout 2 3 import ( 4 "fmt" 5 "net/http" 6 7 "github.com/MakeNowJust/heredoc" 8 "github.com/ungtb10d/cli/v2/api" 9 "github.com/ungtb10d/cli/v2/internal/config" 10 "github.com/ungtb10d/cli/v2/pkg/cmd/auth/shared" 11 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 12 "github.com/ungtb10d/cli/v2/pkg/iostreams" 13 "github.com/spf13/cobra" 14 ) 15 16 type LogoutOptions struct { 17 HttpClient func() (*http.Client, error) 18 IO *iostreams.IOStreams 19 Config func() (config.Config, error) 20 Prompter shared.Prompt 21 Hostname string 22 } 23 24 func NewCmdLogout(f *cmdutil.Factory, runF func(*LogoutOptions) error) *cobra.Command { 25 opts := &LogoutOptions{ 26 HttpClient: f.HttpClient, 27 IO: f.IOStreams, 28 Config: f.Config, 29 Prompter: f.Prompter, 30 } 31 32 cmd := &cobra.Command{ 33 Use: "logout", 34 Args: cobra.ExactArgs(0), 35 Short: "Log out of a GitHub host", 36 Long: heredoc.Doc(`Remove authentication for a GitHub host. 37 38 This command removes the authentication configuration for a host either specified 39 interactively or via --hostname. 40 `), 41 Example: heredoc.Doc(` 42 $ gh auth logout 43 # => select what host to log out of via a prompt 44 45 $ gh auth logout --hostname enterprise.internal 46 # => log out of specified host 47 `), 48 RunE: func(cmd *cobra.Command, args []string) error { 49 if opts.Hostname == "" && !opts.IO.CanPrompt() { 50 return cmdutil.FlagErrorf("--hostname required when not running interactively") 51 } 52 if runF != nil { 53 return runF(opts) 54 } 55 56 return logoutRun(opts) 57 }, 58 } 59 60 cmd.Flags().StringVarP(&opts.Hostname, "hostname", "h", "", "The hostname of the GitHub instance to log out of") 61 62 return cmd 63 } 64 65 func logoutRun(opts *LogoutOptions) error { 66 hostname := opts.Hostname 67 68 cfg, err := opts.Config() 69 if err != nil { 70 return err 71 } 72 73 candidates := cfg.Hosts() 74 if len(candidates) == 0 { 75 return fmt.Errorf("not logged in to any hosts") 76 } 77 78 if hostname == "" { 79 if len(candidates) == 1 { 80 hostname = candidates[0] 81 } else { 82 selected, err := opts.Prompter.Select( 83 "What account do you want to log out of?", "", candidates) 84 if err != nil { 85 return fmt.Errorf("could not prompt: %w", err) 86 } 87 hostname = candidates[selected] 88 } 89 } else { 90 var found bool 91 for _, c := range candidates { 92 if c == hostname { 93 found = true 94 break 95 } 96 } 97 98 if !found { 99 return fmt.Errorf("not logged into %s", hostname) 100 } 101 } 102 103 if src, writeable := shared.AuthTokenWriteable(cfg, hostname); !writeable { 104 fmt.Fprintf(opts.IO.ErrOut, "The value of the %s environment variable is being used for authentication.\n", src) 105 fmt.Fprint(opts.IO.ErrOut, "To erase credentials stored in GitHub CLI, first clear the value from the environment.\n") 106 return cmdutil.SilentError 107 } 108 109 httpClient, err := opts.HttpClient() 110 if err != nil { 111 return err 112 } 113 apiClient := api.NewClientFromHTTP(httpClient) 114 115 username, err := api.CurrentLoginName(apiClient, hostname) 116 if err != nil { 117 // suppressing; the user is trying to delete this token and it might be bad. 118 // we'll see if the username is in the config and fall back to that. 119 username, _ = cfg.Get(hostname, "user") 120 } 121 122 usernameStr := "" 123 if username != "" { 124 usernameStr = fmt.Sprintf(" account '%s'", username) 125 } 126 127 cfg.UnsetHost(hostname) 128 err = cfg.Write() 129 if err != nil { 130 return fmt.Errorf("failed to write config, authentication configuration not updated: %w", err) 131 } 132 133 isTTY := opts.IO.IsStdinTTY() && opts.IO.IsStdoutTTY() 134 135 if isTTY { 136 cs := opts.IO.ColorScheme() 137 fmt.Fprintf(opts.IO.ErrOut, "%s Logged out of %s%s\n", 138 cs.SuccessIcon(), cs.Bold(hostname), usernameStr) 139 } 140 141 return nil 142 }