From 3ffe199ef3e801eaac1775c02170fc47a7b2a2fa Mon Sep 17 00:00:00 2001 From: Benjamin Levesque <14175665+benjlevesque@users.noreply.github.com> Date: Wed, 20 Aug 2025 01:04:09 +0200 Subject: [PATCH] do not fetch scope if not necessary --- pkg/cmd/auth/status/status.go | 82 +++++++++++++++++------------- pkg/cmd/auth/status/status_test.go | 65 +++++++++-------------- 2 files changed, 70 insertions(+), 77 deletions(-) diff --git a/pkg/cmd/auth/status/status.go b/pkg/cmd/auth/status/status.go index cc6f13d49..95597ac53 100644 --- a/pkg/cmd/auth/status/status.go +++ b/pkg/cmd/auth/status/status.go @@ -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") +} diff --git a/pkg/cmd/auth/status/status_test.go b/pkg/cmd/auth/status/status_test.go index 84271a260..64a4868f7 100644 --- a/pkg/cmd/auth/status/status_test.go +++ b/pkg/cmd/auth/status/status_test.go @@ -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"}) + +}