diff --git a/internal/config/from_env.go b/internal/config/from_env.go index 97a5e5b41..b2b02b39f 100644 --- a/internal/config/from_env.go +++ b/internal/config/from_env.go @@ -106,3 +106,11 @@ func AuthTokenProvidedFromEnv() bool { os.Getenv(GH_TOKEN) != "" || os.Getenv(GITHUB_TOKEN) != "" } + +func IsHostEnv(src string) bool { + return src == GH_HOST +} + +func IsEnterpriseEnv(src string) bool { + return src == GH_ENTERPRISE_TOKEN || src == GITHUB_ENTERPRISE_TOKEN +} diff --git a/internal/config/from_env_test.go b/internal/config/from_env_test.go index a3ad8fee4..765cd0160 100644 --- a/internal/config/from_env_test.go +++ b/internal/config/from_env_test.go @@ -44,7 +44,7 @@ func TestInheritEnv(t *testing.T) { baseConfig: ``, hostname: "github.com", wants: wants{ - hosts: []string(nil), + hosts: []string{}, token: "", source: ".config.gh.config.yml", writeable: true, @@ -104,7 +104,7 @@ func TestInheritEnv(t *testing.T) { GITHUB_ENTERPRISE_TOKEN: "ENTOKEN", hostname: "example.org", wants: wants{ - hosts: []string(nil), + hosts: []string{}, token: "ENTOKEN", source: "GITHUB_ENTERPRISE_TOKEN", writeable: false, @@ -116,7 +116,7 @@ func TestInheritEnv(t *testing.T) { GH_ENTERPRISE_TOKEN: "ENTOKEN", hostname: "example.org", wants: wants{ - hosts: []string(nil), + hosts: []string{}, token: "ENTOKEN", source: "GH_ENTERPRISE_TOKEN", writeable: false, @@ -221,7 +221,7 @@ func TestInheritEnv(t *testing.T) { GITHUB_ENTERPRISE_TOKEN: "GITHUBTOKEN", hostname: "example.org", wants: wants{ - hosts: []string(nil), + hosts: []string{}, token: "GHTOKEN", source: "GH_ENTERPRISE_TOKEN", writeable: false, diff --git a/internal/config/from_file.go b/internal/config/from_file.go index b6b7d5a0e..f5a97930f 100644 --- a/internal/config/from_file.go +++ b/internal/config/from_file.go @@ -104,7 +104,7 @@ func (c *fileConfig) UnsetHost(hostname string) { func (c *fileConfig) configForHost(hostname string) (*HostConfig, error) { hosts, err := c.hostEntries() if err != nil { - return nil, fmt.Errorf("failed to parse hosts config: %w", err) + return nil, err } for _, hc := range hosts { @@ -209,7 +209,7 @@ func (c *fileConfig) Aliases() (*AliasConfig, error) { func (c *fileConfig) hostEntries() ([]*HostConfig, error) { entry, err := c.FindEntry("hosts") if err != nil { - return nil, fmt.Errorf("could not find hosts config: %w", err) + return []*HostConfig{}, nil } hostConfigs, err := c.parseHosts(entry.ValueNode) diff --git a/internal/config/from_file_test.go b/internal/config/from_file_test.go new file mode 100644 index 000000000..0c43c43a7 --- /dev/null +++ b/internal/config/from_file_test.go @@ -0,0 +1,15 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_fileConfig_Hosts(t *testing.T) { + c := NewBlankConfig() + hosts, err := c.Hosts() + require.NoError(t, err) + assert.Equal(t, []string{}, hosts) +} diff --git a/pkg/cmd/auth/logout/logout.go b/pkg/cmd/auth/logout/logout.go index 1454a3656..8ad1263a9 100644 --- a/pkg/cmd/auth/logout/logout.go +++ b/pkg/cmd/auth/logout/logout.go @@ -74,6 +74,9 @@ func logoutRun(opts *LogoutOptions) error { candidates, err := cfg.Hosts() if err != nil { + return err + } + if len(candidates) == 0 { return fmt.Errorf("not logged in to any hosts") } diff --git a/pkg/cmd/auth/refresh/refresh.go b/pkg/cmd/auth/refresh/refresh.go index 58267182c..2763679f8 100644 --- a/pkg/cmd/auth/refresh/refresh.go +++ b/pkg/cmd/auth/refresh/refresh.go @@ -83,6 +83,9 @@ func refreshRun(opts *RefreshOptions) error { candidates, err := cfg.Hosts() if err != nil { + return err + } + if len(candidates) == 0 { return fmt.Errorf("not logged in to any hosts. Use 'gh auth login' to authenticate with a host") } diff --git a/pkg/cmd/auth/status/status.go b/pkg/cmd/auth/status/status.go index 6c31cfad4..2be0813c0 100644 --- a/pkg/cmd/auth/status/status.go +++ b/pkg/cmd/auth/status/status.go @@ -69,7 +69,10 @@ func statusRun(opts *StatusOptions) error { statusInfo := map[string][]string{} hostnames, err := cfg.Hosts() - if len(hostnames) == 0 || err != nil { + if err != nil { + return err + } + if len(hostnames) == 0 { fmt.Fprintf(stderr, "You are not logged into any GitHub hosts. Run %s to authenticate.\n", cs.Bold("gh auth login")) return cmdutil.SilentError diff --git a/pkg/cmd/factory/remote_resolver.go b/pkg/cmd/factory/remote_resolver.go index 4a512671a..0de709c29 100644 --- a/pkg/cmd/factory/remote_resolver.go +++ b/pkg/cmd/factory/remote_resolver.go @@ -2,6 +2,7 @@ package factory import ( "errors" + "fmt" "net/url" "sort" @@ -12,8 +13,6 @@ import ( "github.com/cli/cli/pkg/set" ) -const GH_HOST = "GH_HOST" - type remoteResolver struct { readRemotes func() (git.RemoteSet, error) getConfig func() (config.Config, error) @@ -75,18 +74,19 @@ func (rr *remoteResolver) Resolver() func() (context.Remotes, error) { // For enviornment default host (GH_HOST) do not fallback to cachedRemotes if none match if src != "" { filteredRemotes := cachedRemotes.FilterByHosts([]string{defaultHost}) - if src == GH_HOST || len(filteredRemotes) > 0 { + if config.IsHostEnv(src) || len(filteredRemotes) > 0 { cachedRemotes = filteredRemotes } } if len(cachedRemotes) == 0 { - if src == GH_HOST { - remotesError = errors.New("none of the git remotes configured for this repository correspond to the GH_HOST environment variable. Try adding a matching remote or unsetting the variable.") - } else { - 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`") + dummyHostname := "example.com" // any non-github.com hostname is fine here + if config.IsHostEnv(src) { + return nil, fmt.Errorf("none of the git remotes configured for this repository correspond to the %s environment variable. Try adding a matching remote or unsetting the variable.", src) + } else if v, src, _ := cfg.GetWithSource(dummyHostname, "oauth_token"); v != "" && config.IsEnterpriseEnv(src) { + return nil, errors.New("set the GH_HOST environment variable to specify which GitHub host to use") } - return nil, remotesError + return nil, 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 cachedRemotes, nil diff --git a/pkg/cmd/root/help_topic.go b/pkg/cmd/root/help_topic.go index 2edea8ee5..9d512c79f 100644 --- a/pkg/cmd/root/help_topic.go +++ b/pkg/cmd/root/help_topic.go @@ -33,14 +33,14 @@ var HelpTopics = map[string]map[string]string{ previously stored credentials. GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN (in order of precedence): an authentication - token for API requests to GitHub Enterprise. + token for API requests to GitHub Enterprise. When setting this, also set GH_HOST. + + GH_HOST: specify the GitHub hostname for commands that would otherwise assume the + "github.com" host when not in a context of an existing repository. GH_REPO: specify the GitHub repository in the "[HOST/]OWNER/REPO" format for commands that otherwise operate on a local repository. - GH_HOST: specify the GitHub hostname for commands that would otherwise assume - the "github.com" host when not in a context of an existing repository. - GH_EDITOR, GIT_EDITOR, VISUAL, EDITOR (in order of precedence): the editor tool to use for authoring text.