cli/pkg/cmd/auth/status/status.go
Mislav Marohnić c80292c2e8 Extend Config object with GITHUB_TOKEN support
Adding GITHUB_TOKEN & GITHUB_ENTERPRISE_TOKEN support orthogonal to
Config was getting out of hand, especially in `auth` commands that
adjust their messaging and error status based on the presence of these
environment variables.

The new approach builds in support for tokens from environment straight
into Config object by composition. Thus, commands need not ever be
concerned with any specific environment variables.
2020-09-07 21:33:26 +02:00

150 lines
3.9 KiB
Go

package status
import (
"errors"
"fmt"
"net/http"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/api"
"github.com/cli/cli/internal/config"
"github.com/cli/cli/pkg/cmdutil"
"github.com/cli/cli/pkg/iostreams"
"github.com/cli/cli/utils"
"github.com/spf13/cobra"
)
type StatusOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
Config func() (config.Config, error)
Hostname string
}
func NewCmdStatus(f *cmdutil.Factory, runF func(*StatusOptions) error) *cobra.Command {
opts := &StatusOptions{
HttpClient: f.HttpClient,
IO: f.IOStreams,
Config: f.Config,
}
cmd := &cobra.Command{
Use: "status",
Args: cobra.ExactArgs(0),
Short: "View authentication status",
Long: heredoc.Doc(`Verifies and displays information about your authentication state.
This command will test your authentication state for each GitHub host that gh knows about and
report on any issues.
`),
RunE: func(cmd *cobra.Command, args []string) error {
if runF != nil {
return runF(opts)
}
return statusRun(opts)
},
}
cmd.Flags().StringVarP(&opts.Hostname, "hostname", "h", "", "Check a specific hostname's auth status")
return cmd
}
func statusRun(opts *StatusOptions) error {
cfg, err := opts.Config()
if err != nil {
return err
}
// TODO check tty
stderr := opts.IO.ErrOut
statusInfo := map[string][]string{}
hostnames, err := cfg.Hosts()
if len(hostnames) == 0 || err != nil {
fmt.Fprintf(stderr,
"You are not logged into any GitHub hosts. Run %s to authenticate.\n", utils.Bold("gh auth login"))
return cmdutil.SilentError
}
httpClient, err := opts.HttpClient()
if err != nil {
return err
}
apiClient := api.NewClientFromHTTP(httpClient)
var failed bool
for _, hostname := range hostnames {
if opts.Hostname != "" && opts.Hostname != hostname {
continue
}
_, tokenSource, _ := cfg.GetWithSource(hostname, "oauth_token")
tokenIsWriteable := cfg.CheckWriteable(hostname, "oauth_token") == nil
statusInfo[hostname] = []string{}
addMsg := func(x string, ys ...interface{}) {
statusInfo[hostname] = append(statusInfo[hostname], fmt.Sprintf(x, ys...))
}
err = apiClient.HasMinimumScopes(hostname)
if err != nil {
var missingScopes *api.MissingScopesError
if errors.As(err, &missingScopes) {
addMsg("%s %s: %s", utils.Red("X"), hostname, err)
if tokenIsWriteable {
addMsg("- To request missing scopes, run: %s %s\n",
utils.Bold("gh auth refresh -h"),
utils.Bold(hostname))
}
} else {
addMsg("%s %s: authentication failed", utils.Red("X"), hostname)
addMsg("- The %s token in %s is no longer valid.", utils.Bold(hostname), tokenSource)
if tokenIsWriteable {
addMsg("- To re-authenticate, run: %s %s",
utils.Bold("gh auth login -h"), utils.Bold(hostname))
addMsg("- To forget about this host, run: %s %s",
utils.Bold("gh auth logout -h"), utils.Bold(hostname))
}
}
failed = true
} else {
username, err := api.CurrentLoginName(apiClient, hostname)
if err != nil {
addMsg("%s %s: api call failed: %s", utils.Red("X"), hostname, err)
}
addMsg("%s Logged in to %s as %s (%s)", utils.GreenCheck(), hostname, utils.Bold(username), tokenSource)
proto, _ := cfg.Get(hostname, "git_protocol")
if proto != "" {
addMsg("%s Git operations for %s configured to use %s protocol.",
utils.GreenCheck(), hostname, utils.Bold(proto))
}
}
addMsg("")
// NB we could take this opportunity to add or fix the "user" key in the hosts config. I chose
// not to since I wanted this command to be read-only.
}
for _, hostname := range hostnames {
lines, ok := statusInfo[hostname]
if !ok {
continue
}
fmt.Fprintf(stderr, "%s\n", utils.Bold(hostname))
for _, line := range lines {
fmt.Fprintf(stderr, " %s\n", line)
}
}
if failed {
return cmdutil.SilentError
}
return nil
}