Skip to content

Commit d876cf1

Browse files
committed
Merge remote-tracking branch 'origin' into issue-1456__clone-flag-help
2 parents d24f33b + a7b450b commit d876cf1

File tree

106 files changed

+7085
-4686
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+7085
-4686
lines changed

.github/workflows/releases.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
if: "!contains(github.ref, '-')" # skip prereleases
3333
with:
3434
formula-name: gh
35+
download-url: https://github.com/cli/cli.git
3536
env:
3637
COMMITTER_TOKEN: ${{ secrets.UPLOAD_GITHUB_TOKEN }}
3738
- name: Checkout documentation site

api/client.go

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package api
33
import (
44
"bytes"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"io"
89
"io/ioutil"
@@ -11,6 +12,7 @@ import (
1112
"regexp"
1213
"strings"
1314

15+
"github.com/cli/cli/internal/ghinstance"
1416
"github.com/henvic/httpretty"
1517
"github.com/shurcooL/graphql"
1618
)
@@ -43,25 +45,21 @@ func NewClientFromHTTP(httpClient *http.Client) *Client {
4345
func AddHeader(name, value string) ClientOption {
4446
return func(tr http.RoundTripper) http.RoundTripper {
4547
return &funcTripper{roundTrip: func(req *http.Request) (*http.Response, error) {
46-
// prevent the token from leaking to non-GitHub hosts
47-
// TODO: GHE support
48-
if !strings.EqualFold(name, "Authorization") || strings.HasSuffix(req.URL.Hostname(), ".github.com") {
49-
req.Header.Add(name, value)
50-
}
48+
req.Header.Add(name, value)
5149
return tr.RoundTrip(req)
5250
}}
5351
}
5452
}
5553

5654
// AddHeaderFunc is an AddHeader that gets the string value from a function
57-
func AddHeaderFunc(name string, value func() string) ClientOption {
55+
func AddHeaderFunc(name string, getValue func(*http.Request) (string, error)) ClientOption {
5856
return func(tr http.RoundTripper) http.RoundTripper {
5957
return &funcTripper{roundTrip: func(req *http.Request) (*http.Response, error) {
60-
// prevent the token from leaking to non-GitHub hosts
61-
// TODO: GHE support
62-
if !strings.EqualFold(name, "Authorization") || strings.HasSuffix(req.URL.Hostname(), ".github.com") {
63-
req.Header.Add(name, value())
58+
value, err := getValue(req)
59+
if err != nil {
60+
return nil, err
6461
}
62+
req.Header.Add(name, value)
6563
return tr.RoundTrip(req)
6664
}}
6765
}
@@ -198,19 +196,18 @@ func (err HTTPError) Error() string {
198196
return fmt.Sprintf("HTTP %d (%s)", err.StatusCode, err.RequestURL)
199197
}
200198

201-
// Returns whether or not scopes are present, appID, and error
202-
func (c Client) HasScopes(wantedScopes ...string) (bool, string, error) {
203-
url := "https://api.github.com/user"
204-
req, err := http.NewRequest("GET", url, nil)
199+
func (c Client) HasMinimumScopes(hostname string) (bool, error) {
200+
apiEndpoint := ghinstance.RESTPrefix(hostname)
201+
202+
req, err := http.NewRequest("GET", apiEndpoint, nil)
205203
if err != nil {
206-
return false, "", err
204+
return false, err
207205
}
208206

209207
req.Header.Set("Content-Type", "application/json; charset=utf-8")
210-
211208
res, err := c.http.Do(req)
212209
if err != nil {
213-
return false, "", err
210+
return false, err
214211
}
215212

216213
defer func() {
@@ -221,37 +218,46 @@ func (c Client) HasScopes(wantedScopes ...string) (bool, string, error) {
221218
}()
222219

223220
if res.StatusCode != 200 {
224-
return false, "", handleHTTPError(res)
221+
return false, handleHTTPError(res)
225222
}
226223

227-
appID := res.Header.Get("X-Oauth-Client-Id")
228224
hasScopes := strings.Split(res.Header.Get("X-Oauth-Scopes"), ",")
229225

230-
found := 0
226+
search := map[string]bool{
227+
"repo": false,
228+
"read:org": false,
229+
"admin:org": false,
230+
}
231+
231232
for _, s := range hasScopes {
232-
for _, w := range wantedScopes {
233-
if w == strings.TrimSpace(s) {
234-
found++
235-
}
236-
}
233+
search[strings.TrimSpace(s)] = true
237234
}
238235

239-
if found == len(wantedScopes) {
240-
return true, appID, nil
236+
errorMsgs := []string{}
237+
if !search["repo"] {
238+
errorMsgs = append(errorMsgs, "missing required scope 'repo'")
241239
}
242240

243-
return false, appID, nil
241+
if !search["read:org"] && !search["admin:org"] {
242+
errorMsgs = append(errorMsgs, "missing required scope 'read:org'")
243+
}
244+
245+
if len(errorMsgs) > 0 {
246+
return false, errors.New(strings.Join(errorMsgs, ";"))
247+
}
248+
249+
return true, nil
250+
244251
}
245252

246253
// GraphQL performs a GraphQL request and parses the response
247-
func (c Client) GraphQL(query string, variables map[string]interface{}, data interface{}) error {
248-
url := "https://api.github.com/graphql"
254+
func (c Client) GraphQL(hostname string, query string, variables map[string]interface{}, data interface{}) error {
249255
reqBody, err := json.Marshal(map[string]interface{}{"query": query, "variables": variables})
250256
if err != nil {
251257
return err
252258
}
253259

254-
req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
260+
req, err := http.NewRequest("POST", ghinstance.GraphQLEndpoint(hostname), bytes.NewBuffer(reqBody))
255261
if err != nil {
256262
return err
257263
}
@@ -267,13 +273,13 @@ func (c Client) GraphQL(query string, variables map[string]interface{}, data int
267273
return handleResponse(resp, data)
268274
}
269275

270-
func graphQLClient(h *http.Client) *graphql.Client {
271-
return graphql.NewClient("https://api.github.com/graphql", h)
276+
func graphQLClient(h *http.Client, hostname string) *graphql.Client {
277+
return graphql.NewClient(ghinstance.GraphQLEndpoint(hostname), h)
272278
}
273279

274280
// REST performs a REST request and parses the response.
275-
func (c Client) REST(method string, p string, body io.Reader, data interface{}) error {
276-
url := "https://api.github.com/" + p
281+
func (c Client) REST(hostname string, method string, p string, body io.Reader, data interface{}) error {
282+
url := ghinstance.RESTPrefix(hostname) + p
277283
req, err := http.NewRequest(method, url, body)
278284
if err != nil {
279285
return err

api/client_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestGraphQL(t *testing.T) {
3333
}{}
3434

3535
http.StubResponse(200, bytes.NewBufferString(`{"data":{"viewer":{"login":"hubot"}}}`))
36-
err := client.GraphQL("QUERY", vars, &response)
36+
err := client.GraphQL("github.com", "QUERY", vars, &response)
3737
eq(t, err, nil)
3838
eq(t, response.Viewer.Login, "hubot")
3939

@@ -55,7 +55,7 @@ func TestGraphQLError(t *testing.T) {
5555
]
5656
}`))
5757

58-
err := client.GraphQL("", nil, &response)
58+
err := client.GraphQL("github.com", "", nil, &response)
5959
if err == nil || err.Error() != "GraphQL error: OH NO\nthis is fine" {
6060
t.Fatalf("got %q", err.Error())
6161
}
@@ -71,7 +71,7 @@ func TestRESTGetDelete(t *testing.T) {
7171
http.StubResponse(204, bytes.NewBuffer([]byte{}))
7272

7373
r := bytes.NewReader([]byte(`{}`))
74-
err := client.REST("DELETE", "applications/CLIENTID/grant", r, nil)
74+
err := client.REST("github.com", "DELETE", "applications/CLIENTID/grant", r, nil)
7575
eq(t, err, nil)
7676
}
7777

@@ -82,7 +82,7 @@ func TestRESTError(t *testing.T) {
8282
http.StubResponse(422, bytes.NewBufferString(`{"message": "OH NO"}`))
8383

8484
var httpErr HTTPError
85-
err := client.REST("DELETE", "repos/branch", nil, nil)
85+
err := client.REST("github.com", "DELETE", "repos/branch", nil, nil)
8686
if err == nil || !errors.As(err, &httpErr) {
8787
t.Fatalf("got %v", err)
8888
}

api/queries_gist.go

Lines changed: 0 additions & 52 deletions
This file was deleted.

api/queries_issue.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func IssueCreate(client *Client, repo *Repository, params map[string]interface{}
112112
}
113113
}{}
114114

115-
err := client.GraphQL(query, variables, &result)
115+
err := client.GraphQL(repo.RepoHost(), query, variables, &result)
116116
if err != nil {
117117
return nil, err
118118
}
@@ -171,7 +171,7 @@ func IssueStatus(client *Client, repo ghrepo.Interface, currentUsername string)
171171
}
172172

173173
var resp response
174-
err := client.GraphQL(query, variables, &resp)
174+
err := client.GraphQL(repo.RepoHost(), query, variables, &resp)
175175
if err != nil {
176176
return nil, err
177177
}
@@ -270,7 +270,7 @@ func IssueList(client *Client, repo ghrepo.Interface, state string, labels []str
270270
loop:
271271
for {
272272
variables["limit"] = pageLimit
273-
err := client.GraphQL(query, variables, &response)
273+
err := client.GraphQL(repo.RepoHost(), query, variables, &response)
274274
if err != nil {
275275
return nil, err
276276
}
@@ -361,7 +361,7 @@ func IssueByNumber(client *Client, repo ghrepo.Interface, number int) (*Issue, e
361361
}
362362

363363
var resp response
364-
err := client.GraphQL(query, variables, &resp)
364+
err := client.GraphQL(repo.RepoHost(), query, variables, &resp)
365365
if err != nil {
366366
return nil, err
367367
}
@@ -389,7 +389,7 @@ func IssueClose(client *Client, repo ghrepo.Interface, issue Issue) error {
389389
},
390390
}
391391

392-
gql := graphQLClient(client.http)
392+
gql := graphQLClient(client.http, repo.RepoHost())
393393
err := gql.MutateNamed(context.Background(), "IssueClose", &mutation, variables)
394394

395395
if err != nil {
@@ -414,7 +414,7 @@ func IssueReopen(client *Client, repo ghrepo.Interface, issue Issue) error {
414414
},
415415
}
416416

417-
gql := graphQLClient(client.http)
417+
gql := graphQLClient(client.http, repo.RepoHost())
418418
err := gql.MutateNamed(context.Background(), "IssueReopen", &mutation, variables)
419419

420420
return err

api/queries_org.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package api
33
import (
44
"context"
55

6+
"github.com/cli/cli/internal/ghrepo"
67
"github.com/shurcooL/githubv4"
78
)
89

910
// OrganizationProjects fetches all open projects for an organization
10-
func OrganizationProjects(client *Client, owner string) ([]RepoProject, error) {
11+
func OrganizationProjects(client *Client, repo ghrepo.Interface) ([]RepoProject, error) {
1112
var query struct {
1213
Organization struct {
1314
Projects struct {
@@ -21,11 +22,11 @@ func OrganizationProjects(client *Client, owner string) ([]RepoProject, error) {
2122
}
2223

2324
variables := map[string]interface{}{
24-
"owner": githubv4.String(owner),
25+
"owner": githubv4.String(repo.RepoOwner()),
2526
"endCursor": (*githubv4.String)(nil),
2627
}
2728

28-
gql := graphQLClient(client.http)
29+
gql := graphQLClient(client.http, repo.RepoHost())
2930

3031
var projects []RepoProject
3132
for {
@@ -50,7 +51,7 @@ type OrgTeam struct {
5051
}
5152

5253
// OrganizationTeams fetches all the teams in an organization
53-
func OrganizationTeams(client *Client, owner string) ([]OrgTeam, error) {
54+
func OrganizationTeams(client *Client, repo ghrepo.Interface) ([]OrgTeam, error) {
5455
var query struct {
5556
Organization struct {
5657
Teams struct {
@@ -64,11 +65,11 @@ func OrganizationTeams(client *Client, owner string) ([]OrgTeam, error) {
6465
}
6566

6667
variables := map[string]interface{}{
67-
"owner": githubv4.String(owner),
68+
"owner": githubv4.String(repo.RepoOwner()),
6869
"endCursor": (*githubv4.String)(nil),
6970
}
7071

71-
gql := graphQLClient(client.http)
72+
gql := graphQLClient(client.http, repo.RepoHost())
7273

7374
var teams []OrgTeam
7475
for {

0 commit comments

Comments
 (0)