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.
150 lines
3.9 KiB
Go
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
|
|
}
|