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  }