diff --git a/api/queries_user.go b/api/queries_user.go new file mode 100644 index 000000000..c83d3aac2 --- /dev/null +++ b/api/queries_user.go @@ -0,0 +1,18 @@ +package api + +import ( + "context" + + "github.com/shurcooL/githubv4" +) + +func CurrentLoginName(client *Client) (string, error) { + var query struct { + Viewer struct { + Login string + } + } + v4 := githubv4.NewClient(client.http) + err := v4.Query(context.Background(), &query, nil) + return query.Viewer.Login, err +} diff --git a/command/issue.go b/command/issue.go index 3d3f01708..46648d914 100644 --- a/command/issue.go +++ b/command/issue.go @@ -172,7 +172,7 @@ func issueStatus(cmd *cobra.Command, args []string) error { return err } - currentUser, err := ctx.AuthLogin() + currentUser, err := api.CurrentLoginName(apiClient) if err != nil { return err } diff --git a/command/issue_test.go b/command/issue_test.go index 3df6910f5..0bb2707dc 100644 --- a/command/issue_test.go +++ b/command/issue_test.go @@ -20,6 +20,9 @@ func TestIssueStatus(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") + http.Register( + httpmock.GraphQL(`\bviewer\b`), + httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`)) jsonFile, _ := os.Open("../test/fixtures/issueStatus.json") defer jsonFile.Close() @@ -49,6 +52,9 @@ func TestIssueStatus_blankSlate(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") + http.Register( + httpmock.GraphQL(`\bviewer\b`), + httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`)) http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { @@ -86,6 +92,9 @@ func TestIssueStatus_disabledIssues(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") + http.Register( + httpmock.GraphQL(`\bviewer\b`), + httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`)) http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { diff --git a/command/pr.go b/command/pr.go index fca6fed2a..386a5e8ce 100644 --- a/command/pr.go +++ b/command/pr.go @@ -108,11 +108,6 @@ func prStatus(cmd *cobra.Command, args []string) error { return err } - currentUser, err := ctx.AuthLogin() - if err != nil { - return err - } - baseRepo, err := determineBaseRepo(apiClient, cmd, ctx) if err != nil { return err @@ -125,6 +120,8 @@ func prStatus(cmd *cobra.Command, args []string) error { return fmt.Errorf("could not query for pull request for current branch: %w", err) } + // the `@me` macro is available because the API lookup is ElasticSearch-based + currentUser := "@me" prPayload, err := api.PullRequests(apiClient, baseRepo, currentPRNumber, currentPRHeadRef, currentUser) if err != nil { return err diff --git a/command/root.go b/command/root.go index 3b86c0864..cabf9a7c0 100644 --- a/command/root.go +++ b/command/root.go @@ -101,6 +101,9 @@ var initContext = func() context.Context { if repo := os.Getenv("GH_REPO"); repo != "" { ctx.SetBaseRepo(repo) } + if token := os.Getenv("GITHUB_TOKEN"); token != "" { + ctx.SetAuthToken(token) + } return ctx } @@ -113,11 +116,15 @@ func BasicClient() (*api.Client, error) { } opts = append(opts, api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version))) - if c, err := config.ParseDefaultConfig(); err == nil { - if token, _ := c.Get(defaultHostname, "oauth_token"); token != "" { - opts = append(opts, api.AddHeader("Authorization", fmt.Sprintf("token %s", token))) + token := os.Getenv("GITHUB_TOKEN") + if token == "" { + if c, err := config.ParseDefaultConfig(); err == nil { + token, _ = c.Get(defaultHostname, "oauth_token") } } + if token != "" { + opts = append(opts, api.AddHeader("Authorization", fmt.Sprintf("token %s", token))) + } return api.NewClient(opts...), nil } @@ -145,8 +152,12 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) { return fmt.Sprintf("token %s", token) } + tokenFromEnv := func() bool { + return os.Getenv("GITHUB_TOKEN") == token + } + checkScopesFunc := func(appID string) error { - if config.IsGitHubApp(appID) && utils.IsTerminal(os.Stdin) && utils.IsTerminal(os.Stderr) { + if config.IsGitHubApp(appID) && !tokenFromEnv() && utils.IsTerminal(os.Stdin) && utils.IsTerminal(os.Stderr) { newToken, loginHandle, err := config.AuthFlow("Notice: additional authorization required") if err != nil { return err @@ -168,7 +179,11 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) { } else { fmt.Fprintln(os.Stderr, "Warning: gh now requires the `read:org` OAuth scope.") fmt.Fprintln(os.Stderr, "Visit https://github.com/settings/tokens and edit your token to enable `read:org`") - fmt.Fprintln(os.Stderr, "or generate a new token and paste it via `gh config set -h github.com oauth_token MYTOKEN`") + if tokenFromEnv() { + fmt.Fprintln(os.Stderr, "or generate a new token for the GITHUB_TOKEN environment variable") + } else { + fmt.Fprintln(os.Stderr, "or generate a new token and paste it via `gh config set -h github.com oauth_token MYTOKEN`") + } } return nil } @@ -194,7 +209,9 @@ var ensureScopes = func(ctx context.Context, client *api.Client, wantedScopes .. return client, nil } - if config.IsGitHubApp(appID) && utils.IsTerminal(os.Stdin) && utils.IsTerminal(os.Stderr) { + tokenFromEnv := len(os.Getenv("GITHUB_TOKEN")) > 0 + + if config.IsGitHubApp(appID) && !tokenFromEnv && utils.IsTerminal(os.Stdin) && utils.IsTerminal(os.Stderr) { newToken, loginHandle, err := config.AuthFlow("Notice: additional authorization required") if err != nil { return client, err @@ -219,9 +236,13 @@ var ensureScopes = func(ctx context.Context, client *api.Client, wantedScopes .. return reloadedClient, nil } else { - fmt.Fprintln(os.Stderr, fmt.Sprintf("Warning: gh now requires the `%s` OAuth scope(s).", wantedScopes)) + fmt.Fprintln(os.Stderr, fmt.Sprintf("Warning: gh now requires %s OAuth scopes.", wantedScopes)) fmt.Fprintln(os.Stderr, fmt.Sprintf("Visit https://github.com/settings/tokens and edit your token to enable %s", wantedScopes)) - fmt.Fprintln(os.Stderr, "or generate a new token and paste it via `gh config set -h github.com oauth_token MYTOKEN`") + if tokenFromEnv { + fmt.Fprintln(os.Stderr, "or generate a new token for the GITHUB_TOKEN environment variable") + } else { + fmt.Fprintln(os.Stderr, "or generate a new token and paste it via `gh config set -h github.com oauth_token MYTOKEN`") + } return client, errors.New("Unable to reauthenticate") } diff --git a/context/blank_context.go b/context/blank_context.go index 343f32c7e..ed8784cfc 100644 --- a/context/blank_context.go +++ b/context/blank_context.go @@ -17,7 +17,6 @@ func NewBlank() *blankContext { // A Context implementation that queries the filesystem type blankContext struct { authToken string - authLogin string branch string baseRepo ghrepo.Interface remotes Remotes @@ -39,14 +38,6 @@ func (c *blankContext) SetAuthToken(t string) { c.authToken = t } -func (c *blankContext) SetAuthLogin(login string) { - c.authLogin = login -} - -func (c *blankContext) AuthLogin() (string, error) { - return c.authLogin, nil -} - func (c *blankContext) Branch() (string, error) { if c.branch == "" { return "", fmt.Errorf("branch was not initialized") diff --git a/context/context.go b/context/context.go index 2a25f5dda..ddeb82c4d 100644 --- a/context/context.go +++ b/context/context.go @@ -18,7 +18,6 @@ const defaultHostname = "github.com" type Context interface { AuthToken() (string, error) SetAuthToken(string) - AuthLogin() (string, error) Branch() (string, error) SetBranch(string) Remotes() (Remotes, error) @@ -195,20 +194,6 @@ func (c *fsContext) SetAuthToken(t string) { c.authToken = t } -func (c *fsContext) AuthLogin() (string, error) { - config, err := c.Config() - if err != nil { - return "", err - } - - login, err := config.Get(defaultHostname, "user") - if login == "" || err != nil { - return "", err - } - - return login, nil -} - func (c *fsContext) Branch() (string, error) { if c.branch != "" { return c.branch, nil diff --git a/internal/config/config_setup.go b/internal/config/config_setup.go index f74856b24..c22c07e35 100644 --- a/internal/config/config_setup.go +++ b/internal/config/config_setup.go @@ -116,14 +116,7 @@ func setupConfigFile(filename string) (Config, error) { func getViewer(token string) (string, error) { http := api.NewClient(api.AddHeader("Authorization", fmt.Sprintf("token %s", token))) - - response := struct { - Viewer struct { - Login string - } - }{} - err := http.GraphQL("{ viewer { login } }", nil, &response) - return response.Viewer.Login, err + return api.CurrentLoginName(http) } func waitForEnter(r io.Reader) error {