commit
82c6830849
11 changed files with 139 additions and 18 deletions
|
|
@ -10,6 +10,7 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/cli/api"
|
||||
"github.com/cli/cli/command"
|
||||
"github.com/cli/cli/internal/config"
|
||||
"github.com/cli/cli/internal/ghinstance"
|
||||
|
|
@ -91,10 +92,41 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
authCheckEnabled := cmdutil.IsAuthCheckEnabled(cmd)
|
||||
|
||||
// TODO support other names
|
||||
ghtoken := os.Getenv("GITHUB_TOKEN")
|
||||
if ghtoken != "" {
|
||||
authCheckEnabled = false
|
||||
}
|
||||
|
||||
if authCheckEnabled {
|
||||
hasAuth := false
|
||||
|
||||
cfg, err := cmdFactory.Config()
|
||||
if err == nil {
|
||||
hasAuth = cmdutil.CheckAuth(cfg)
|
||||
}
|
||||
|
||||
if !hasAuth {
|
||||
fmt.Fprintln(stderr, utils.Bold("Welcome to GitHub CLI!"))
|
||||
fmt.Fprintln(stderr)
|
||||
fmt.Fprintln(stderr, "To authenticate, please run `gh auth login`.")
|
||||
fmt.Fprintln(stderr, "You can also set the GITHUB_TOKEN environment variable, if preferred.")
|
||||
os.Exit(4)
|
||||
}
|
||||
}
|
||||
|
||||
rootCmd.SetArgs(expandedArgs)
|
||||
|
||||
if cmd, err := rootCmd.ExecuteC(); err != nil {
|
||||
printError(stderr, err, cmd, hasDebug)
|
||||
|
||||
var httpErr api.HTTPError
|
||||
if errors.As(err, &httpErr) && httpErr.StatusCode == 401 {
|
||||
fmt.Println("hint: try authenticating with `gh auth login`")
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
if root.HasFailed() {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ func NewCmdAlias(f *cmdutil.Factory) *cobra.Command {
|
|||
`),
|
||||
}
|
||||
|
||||
cmdutil.DisableAuthCheck(cmd)
|
||||
|
||||
cmd.AddCommand(deleteCmd.NewCmdDelete(f, nil))
|
||||
cmd.AddCommand(listCmd.NewCmdList(f, nil))
|
||||
cmd.AddCommand(setCmd.NewCmdSet(f, nil))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ func NewCmdAuth(f *cmdutil.Factory) *cobra.Command {
|
|||
Long: `Manage gh's authentication state.`,
|
||||
}
|
||||
|
||||
cmdutil.DisableAuthCheck(cmd)
|
||||
|
||||
cmd.AddCommand(authLoginCmd.NewCmdLogin(f, nil))
|
||||
cmd.AddCommand(authLogoutCmd.NewCmdLogout(f, nil))
|
||||
cmd.AddCommand(authStatusCmd.NewCmdStatus(f, nil))
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ func NewCmdLogin(f *cmdutil.Factory, runF func(*LoginOptions) error) *cobra.Comm
|
|||
# => read token from mytoken.txt and authenticate against github.com
|
||||
|
||||
$ gh auth login --hostname enterprise.internal --with-token < mytoken.txt
|
||||
# => read token from mytoken.txt and authenticate against a GitHub Enterprise instance
|
||||
# => read token from mytoken.txt and authenticate against a GitHub Enterprise Server instance
|
||||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
isTTY := opts.IO.IsStdinTTY()
|
||||
|
|
@ -142,7 +142,7 @@ func loginRun(opts *LoginOptions) error {
|
|||
Message: "What account do you want to log into?",
|
||||
Options: []string{
|
||||
"GitHub.com",
|
||||
"GitHub Enterprise",
|
||||
"GitHub Enterprise Server",
|
||||
},
|
||||
}, &hostType)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ func NewCmdConfig(f *cmdutil.Factory) *cobra.Command {
|
|||
`),
|
||||
}
|
||||
|
||||
cmdutil.DisableAuthCheck(cmd)
|
||||
|
||||
cmd.AddCommand(NewCmdConfigGet(f))
|
||||
cmd.AddCommand(NewCmdConfigSet(f))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package factory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
|
@ -30,20 +29,9 @@ func httpClient(io *iostreams.IOStreams, cfg config.Config, appVersion string, s
|
|||
|
||||
hostname := ghinstance.NormalizeHostname(req.URL.Hostname())
|
||||
token, err := cfg.Get(hostname, "oauth_token")
|
||||
if token == "" {
|
||||
var notFound *config.NotFoundError
|
||||
// TODO: check if stdout is TTY too
|
||||
if errors.As(err, ¬Found) && io.IsStdinTTY() {
|
||||
// interactive OAuth flow
|
||||
token, err = config.AuthFlowWithConfig(cfg, hostname, "Notice: authentication required", nil)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if token == "" {
|
||||
// TODO: instruct user how to manually authenticate
|
||||
return "", fmt.Errorf("authentication required for %s", hostname)
|
||||
}
|
||||
if err != nil || token == "" {
|
||||
// Users shouldn't see this because of the pre-execute auth check on commands
|
||||
return "", fmt.Errorf("authentication required for %s; please run `gh auth login -h %s`", hostname, hostname)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("token %s", token), nil
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ func (rr *remoteResolver) Resolver() func() (context.Remotes, error) {
|
|||
}
|
||||
|
||||
if len(cachedRemotes) == 0 {
|
||||
remotesError = errors.New("none of the git remotes point to a known GitHub host")
|
||||
remotesError = errors.New("none of the git remotes configured for this repository point to a known GitHub host. To tell gh about a new GitHub host, please use `gh auth login`")
|
||||
return nil, remotesError
|
||||
}
|
||||
return cachedRemotes, nil
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ func NewCmdCompletion(io *iostreams.IOStreams) *cobra.Command {
|
|||
},
|
||||
}
|
||||
|
||||
cmdutil.DisableAuthCheck(cmd)
|
||||
|
||||
cmd.Flags().StringVarP(&shellType, "shell", "s", "", "Shell type: {bash|zsh|fish|powershell}")
|
||||
|
||||
return cmd
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) *cobra.Command {
|
|||
return &cmdutil.FlagError{Err: err}
|
||||
})
|
||||
|
||||
cmdutil.DisableAuthCheck(cmd)
|
||||
|
||||
// CHILD COMMANDS
|
||||
|
||||
cmd.AddCommand(aliasCmd.NewCmdAlias(f))
|
||||
|
|
|
|||
46
pkg/cmdutil/auth_check.go
Normal file
46
pkg/cmdutil/auth_check.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package cmdutil
|
||||
|
||||
import (
|
||||
"github.com/cli/cli/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// TODO can have this set a PersistentPreRun so we don't have to set for all child commands of auth,
|
||||
// config
|
||||
|
||||
func DisableAuthCheck(cmd *cobra.Command) {
|
||||
if cmd.Annotations == nil {
|
||||
cmd.Annotations = map[string]string{}
|
||||
}
|
||||
|
||||
cmd.Annotations["skipAuthCheck"] = "true"
|
||||
}
|
||||
|
||||
func CheckAuth(cfg config.Config) bool {
|
||||
hosts, err := cfg.Hosts()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, hostname := range hosts {
|
||||
token, _ := cfg.Get(hostname, "oauth_token")
|
||||
if token != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsAuthCheckEnabled(cmd *cobra.Command) bool {
|
||||
if !cmd.Runnable() {
|
||||
return false
|
||||
}
|
||||
for c := cmd; c.Parent() != nil; c = c.Parent() {
|
||||
if c.Annotations != nil && c.Annotations["skipAuthCheck"] == "true" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
45
pkg/cmdutil/auth_check_test.go
Normal file
45
pkg/cmdutil/auth_check_test.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package cmdutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cli/cli/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_CheckAuth(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg func(config.Config)
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "no hosts",
|
||||
cfg: func(c config.Config) {},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "host, no token",
|
||||
cfg: func(c config.Config) {
|
||||
_ = c.Set("github.com", "oauth_token", "")
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "host, token",
|
||||
cfg: func(c config.Config) {
|
||||
_ = c.Set("github.com", "oauth_token", "a token")
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := config.NewBlankConfig()
|
||||
tt.cfg(cfg)
|
||||
result := CheckAuth(cfg)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue