do not fetch scope if not necessary

This commit is contained in:
Benjamin Levesque 2025-08-20 01:04:09 +02:00
parent 8b553d66cc
commit 3ffe199ef3
No known key found for this signature in database
GPG key ID: 765E3CB147AA4D4A
2 changed files with 70 additions and 77 deletions

View file

@ -25,10 +25,10 @@ type authEntry struct {
Active bool `json:"active"`
Host string `json:"host"`
Login string `json:"login"`
TokenSource string `json:"token_source"`
TokenSource string `json:"tokenSource"`
Token string `json:"token"`
Scopes string `json:"scopes"`
GitProtocol string `json:"git_protocol"`
GitProtocol string `json:"gitProtocol"`
}
var authFields = []string{
@ -37,10 +37,10 @@ var authFields = []string{
"active",
"host",
"login",
"token_source",
"tokenSource",
"token",
"scopes",
"git_protocol",
"gitProtocol",
}
func (e authEntry) String(cs *iostreams.ColorScheme, showToken bool) string {
@ -227,13 +227,14 @@ func statusRun(opts *StatusOptions) error {
activeUser, _ = authCfg.ActiveUser(hostname)
}
entry := buildEntry(httpClient, buildEntryOptions{
active: true,
gitProtocol: gitProtocol,
hostname: hostname,
showToken: opts.ShowToken,
token: activeUserToken,
tokenSource: activeUserTokenSource,
username: activeUser,
active: true,
gitProtocol: gitProtocol,
hostname: hostname,
showToken: opts.ShowToken,
token: activeUserToken,
tokenSource: activeUserTokenSource,
username: activeUser,
includeScope: opts.includeScope(),
})
statuses[hostname] = append(statuses[hostname], entry)
@ -252,13 +253,14 @@ func statusRun(opts *StatusOptions) error {
}
token, tokenSource, _ := authCfg.TokenForUser(hostname, username)
entry := buildEntry(httpClient, buildEntryOptions{
active: false,
gitProtocol: gitProtocol,
hostname: hostname,
showToken: opts.ShowToken,
token: token,
tokenSource: tokenSource,
username: username,
active: false,
gitProtocol: gitProtocol,
hostname: hostname,
showToken: opts.ShowToken,
token: token,
tokenSource: tokenSource,
username: username,
includeScope: opts.includeScope(),
})
statuses[hostname] = append(statuses[hostname], entry)
@ -333,13 +335,14 @@ func expectScopes(token string) bool {
}
type buildEntryOptions struct {
active bool
gitProtocol string
hostname string
showToken bool
token string
tokenSource string
username string
active bool
gitProtocol string
hostname string
showToken bool
token string
tokenSource string
username string
includeScope bool
}
func buildEntry(httpClient *http.Client, opts buildEntryOptions) authEntry {
@ -373,21 +376,23 @@ func buildEntry(httpClient *http.Client, opts buildEntryOptions) authEntry {
}
}
// Get scopes for token.
scopesHeader, err := shared.GetScopes(httpClient, opts.hostname, opts.token)
if err != nil {
var networkError net.Error
if errors.As(err, &networkError) && networkError.Timeout() {
entry.State = AuthStateTimeout
if opts.includeScope {
// Get scopes for token.
scopesHeader, err := shared.GetScopes(httpClient, opts.hostname, opts.token)
if err != nil {
var networkError net.Error
if errors.As(err, &networkError) && networkError.Timeout() {
entry.State = AuthStateTimeout
return entry
}
entry.State = AuthStateError
return entry
}
entry.State = AuthStateError
return entry
entry.Scopes = scopesHeader
}
entry.State = AuthStateSuccess
entry.Scopes = scopesHeader
return entry
}
@ -398,3 +403,10 @@ func authTokenWriteable(src string) bool {
func isValidEntry(entry authEntry) bool {
return entry.State == AuthStateSuccess
}
func (opts *StatusOptions) includeScope() bool {
if opts.Exporter == nil {
return true
}
return slices.Contains(opts.Exporter.Fields(), "scopes")
}

View file

@ -531,7 +531,7 @@ func Test_statusRun(t *testing.T) {
`),
},
{
name: "No tokens, with json flag",
name: "No tokens with json flag",
opts: StatusOptions{
Exporter: defaultJsonExporter(),
},
@ -540,7 +540,7 @@ func Test_statusRun(t *testing.T) {
wantErrOut: "You are not logged into any GitHub hosts. To log in, run: gh auth login\n",
},
{
name: "No token for the given --hostname, with json flag",
name: "No token for the given --hostname with json flag",
opts: StatusOptions{
Hostname: "foo.com",
Exporter: defaultJsonExporter(),
@ -553,7 +553,7 @@ func Test_statusRun(t *testing.T) {
wantErrOut: "You are not logged into any accounts on foo.com\n",
},
{
name: "All valid tokens, with json flag",
name: "All valid tokens with json flag",
opts: StatusOptions{
Exporter: defaultJsonExporter(),
},
@ -562,17 +562,6 @@ func Test_statusRun(t *testing.T) {
login(t, c, "github.com", "monalisa2", "gho_abc123", "https")
login(t, c, "ghe.io", "monalisa-ghe", "gho_abc123", "https")
},
httpStubs: func(reg *httpmock.Registry) {
// mocks for HeaderHasMinimumScopes api requests to github.com
reg.Register(
httpmock.REST("GET", ""),
httpmock.WithHeader(httpmock.ScopesResponder("repo,read:org"), "X-Oauth-Scopes", "repo, read:org"))
reg.Register(
httpmock.REST("GET", ""),
httpmock.WithHeader(httpmock.ScopesResponder("repo,read:org"), "X-Oauth-Scopes", "repo, read:org"))
reg.Register(httpmock.REST("GET", "api/v3/"), httpmock.WithHeader(httpmock.ScopesResponder("repo,read:org"), "X-Oauth-Scopes", "repo, read:org"))
},
wantOut: `{` +
`"ghe.io":[` +
`{"active":true,"host":"ghe.io","login":"monalisa-ghe","state":"success"}` +
@ -583,7 +572,7 @@ func Test_statusRun(t *testing.T) {
"]}\n",
},
{
name: "All valid tokens, with hostname and json flag",
name: "All valid tokens with hostname and json flag",
opts: StatusOptions{
Hostname: "github.com",
Exporter: defaultJsonExporter(),
@ -593,16 +582,6 @@ func Test_statusRun(t *testing.T) {
login(t, c, "github.com", "monalisa2", "gho_abc123", "https")
login(t, c, "ghe.io", "monalisa-ghe", "gho_abc123", "https")
},
httpStubs: func(reg *httpmock.Registry) {
// mocks for HeaderHasMinimumScopes api requests to github.com
reg.Register(
httpmock.REST("GET", ""),
httpmock.WithHeader(httpmock.ScopesResponder("repo,read:org"), "X-Oauth-Scopes", "repo, read:org"))
reg.Register(
httpmock.REST("GET", ""),
httpmock.WithHeader(httpmock.ScopesResponder("repo,read:org"), "X-Oauth-Scopes", "repo, read:org"))
},
wantOut: `{` +
`"github.com":[` +
`{"active":true,"host":"github.com","login":"monalisa2","state":"success"},` +
@ -610,7 +589,7 @@ func Test_statusRun(t *testing.T) {
"]}\n",
},
{
name: "All valid tokens, with active and json flag",
name: "All valid tokens with active and json flag",
opts: StatusOptions{
Active: true,
Exporter: defaultJsonExporter(),
@ -620,13 +599,6 @@ func Test_statusRun(t *testing.T) {
login(t, c, "github.com", "monalisa2", "gho_abc123", "https")
login(t, c, "ghe.io", "monalisa-ghe", "gho_abc123", "https")
},
httpStubs: func(reg *httpmock.Registry) {
// mocks for HeaderHasMinimumScopes api requests to github.com
reg.Register(
httpmock.REST("GET", ""),
httpmock.WithHeader(httpmock.ScopesResponder("repo,read:org"), "X-Oauth-Scopes", "repo, read:org"))
reg.Register(httpmock.REST("GET", "api/v3/"), httpmock.WithHeader(httpmock.ScopesResponder("repo,read:org"), "X-Oauth-Scopes", "repo, read:org"))
},
wantOut: `{` +
`"ghe.io":[` +
`{"active":true,"host":"ghe.io","login":"monalisa-ghe","state":"success"}` +
@ -636,9 +608,9 @@ func Test_statusRun(t *testing.T) {
"]}\n",
},
{
name: "bad token, with json flag",
name: "bad token with json flag",
opts: StatusOptions{
Exporter: defaultJsonExporter(),
Exporter: addFieldsToExporter(defaultJsonExporter(), []string{"scopes"}),
},
cfgStubs: func(t *testing.T, c gh.Config) {
login(t, c, "ghe.io", "monalisa-ghe", "gho_abc123", "https")
@ -648,13 +620,13 @@ func Test_statusRun(t *testing.T) {
reg.Register(httpmock.REST("GET", "api/v3/"), httpmock.StatusStringResponse(400, "no bueno"))
},
wantErr: cmdutil.SilentError,
wantOut: `{"ghe.io":[{"active":true,"host":"ghe.io","login":"monalisa-ghe","state":"error"}]}` + "\n",
wantOut: `{"ghe.io":[{"active":true,"host":"ghe.io","login":"monalisa-ghe","scopes":"","state":"error"}]}` + "\n",
},
{
name: "timeout error, with json flag",
name: "timeout error with json flag",
opts: StatusOptions{
Hostname: "github.com",
Exporter: defaultJsonExporter(),
Exporter: addFieldsToExporter(defaultJsonExporter(), []string{"scopes"}),
},
cfgStubs: func(t *testing.T, c gh.Config) {
login(t, c, "github.com", "monalisa", "abc123", "https")
@ -666,7 +638,7 @@ func Test_statusRun(t *testing.T) {
})
},
wantErr: cmdutil.SilentError,
wantOut: `{"github.com":[{"active":true,"host":"github.com","login":"monalisa","state":"timeout"}]}` + "\n",
wantOut: `{"github.com":[{"active":true,"host":"github.com","login":"monalisa","scopes":"","state":"timeout"}]}` + "\n",
},
// TODO: is MarkFlagsMutuallyExclusive ok?
@ -734,8 +706,17 @@ func login(t *testing.T, c gh.Config, hostname, username, protocol, token string
require.NoError(t, err)
}
func defaultJsonExporter() cmdutil.Exporter {
e := cmdutil.NewJSONExporter()
e.SetFields([]string{"login", "host", "state", "active"})
type exporter interface {
cmdutil.Exporter
SetFields(fields []string)
}
func addFieldsToExporter(e exporter, fields []string) exporter {
newFields := append(e.Fields(), fields...)
e.SetFields(newFields)
return e
}
func defaultJsonExporter() exporter {
return addFieldsToExporter(cmdutil.NewJSONExporter(), []string{"login", "host", "state", "active"})
}