Skip to content

Commit 52cc04e

Browse files
committed
Merge remote-tracking branch 'origin/master' into repo-create
2 parents be09717 + c610f5c commit 52cc04e

26 files changed

+803
-84
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
# VS Code
1010
.vscode
1111

12+
# IntelliJ
13+
.idea
14+
1215
# macOS
1316
.DS_Store
1417

api/fake_http.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
"io"
77
"io/ioutil"
88
"net/http"
9+
"os"
10+
"path"
11+
"strings"
912
)
1013

1114
// FakeHTTP provides a mechanism by which to stub HTTP responses through
@@ -37,6 +40,13 @@ func (f *FakeHTTP) RoundTrip(req *http.Request) (*http.Response, error) {
3740
return resp, nil
3841
}
3942

43+
func (f *FakeHTTP) StubWithFixture(status int, fixtureFileName string) func() {
44+
fixturePath := path.Join("../test/fixtures/", fixtureFileName)
45+
fixtureFile, _ := os.Open(fixturePath)
46+
f.StubResponse(status, fixtureFile)
47+
return func() { fixtureFile.Close() }
48+
}
49+
4050
func (f *FakeHTTP) StubRepoResponse(owner, repo string) {
4151
body := bytes.NewBufferString(fmt.Sprintf(`
4252
{ "data": { "repo_000": {
@@ -56,3 +66,35 @@ func (f *FakeHTTP) StubRepoResponse(owner, repo string) {
5666
}
5767
f.responseStubs = append(f.responseStubs, resp)
5868
}
69+
70+
func (f *FakeHTTP) StubForkedRepoResponse(forkFullName, parentFullName string) {
71+
forkRepo := strings.SplitN(forkFullName, "/", 2)
72+
parentRepo := strings.SplitN(parentFullName, "/", 2)
73+
body := bytes.NewBufferString(fmt.Sprintf(`
74+
{ "data": { "repo_000": {
75+
"id": "REPOID2",
76+
"name": "%s",
77+
"owner": {"login": "%s"},
78+
"defaultBranchRef": {
79+
"name": "master",
80+
"target": {"oid": "deadbeef"}
81+
},
82+
"viewerPermission": "ADMIN",
83+
"parent": {
84+
"id": "REPOID1",
85+
"name": "%s",
86+
"owner": {"login": "%s"},
87+
"defaultBranchRef": {
88+
"name": "master",
89+
"target": {"oid": "deadbeef"}
90+
},
91+
"viewerPermission": "READ"
92+
}
93+
} } }
94+
`, forkRepo[1], forkRepo[0], parentRepo[1], parentRepo[0]))
95+
resp := &http.Response{
96+
StatusCode: 200,
97+
Body: ioutil.NopCloser(body),
98+
}
99+
f.responseStubs = append(f.responseStubs, resp)
100+
}

api/queries_pr.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
type PullRequestsPayload struct {
1111
ViewerCreated PullRequestAndTotalCount
1212
ReviewRequested PullRequestAndTotalCount
13-
CurrentPR *PullRequest
13+
CurrentPRs []PullRequest
1414
}
1515

1616
type PullRequestAndTotalCount struct {
@@ -40,6 +40,7 @@ type PullRequest struct {
4040
}
4141
}
4242
IsCrossRepository bool
43+
IsDraft bool
4344
MaintainerCanModify bool
4445

4546
ReviewDecision string
@@ -80,7 +81,7 @@ type PullRequestReviewStatus struct {
8081
}
8182

8283
func (pr *PullRequest) ReviewStatus() PullRequestReviewStatus {
83-
status := PullRequestReviewStatus{}
84+
var status PullRequestReviewStatus
8485
switch pr.ReviewDecision {
8586
case "CHANGES_REQUESTED":
8687
status.ChangesRequested = true
@@ -150,12 +151,14 @@ func PullRequests(client *Client, repo ghrepo.Interface, currentPRNumber int, cu
150151
fragment pr on PullRequest {
151152
number
152153
title
154+
state
153155
url
154156
headRefName
155157
headRepositoryOwner {
156158
login
157159
}
158160
isCrossRepository
161+
isDraft
159162
commits(last: 1) {
160163
nodes {
161164
commit {
@@ -185,7 +188,7 @@ func PullRequests(client *Client, repo ghrepo.Interface, currentPRNumber int, cu
185188
queryPrefix := `
186189
query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) {
187190
repository(owner: $owner, name: $repo) {
188-
pullRequests(headRefName: $headRefName, states: OPEN, first: $per_page) {
191+
pullRequests(headRefName: $headRefName, first: $per_page, orderBy: { field: CREATED_AT, direction: DESC }) {
189192
totalCount
190193
edges {
191194
node {
@@ -259,11 +262,13 @@ func PullRequests(client *Client, repo ghrepo.Interface, currentPRNumber int, cu
259262
reviewRequested = append(reviewRequested, edge.Node)
260263
}
261264

262-
var currentPR = resp.Repository.PullRequest
263-
if currentPR == nil {
265+
var currentPRs []PullRequest
266+
if resp.Repository.PullRequest != nil {
267+
currentPRs = append(currentPRs, *resp.Repository.PullRequest)
268+
} else {
264269
for _, edge := range resp.Repository.PullRequests.Edges {
265270
if edge.Node.HeadLabel() == currentPRHeadRef {
266-
currentPR = &edge.Node
271+
currentPRs = append(currentPRs, edge.Node)
267272
}
268273
}
269274
}
@@ -277,7 +282,7 @@ func PullRequests(client *Client, repo ghrepo.Interface, currentPRNumber int, cu
277282
PullRequests: reviewRequested,
278283
TotalCount: resp.ReviewRequested.TotalCount,
279284
},
280-
CurrentPR: currentPR,
285+
CurrentPRs: currentPRs,
281286
}
282287

283288
return &payload, nil
@@ -366,6 +371,7 @@ func PullRequestForBranch(client *Client, repo ghrepo.Interface, branch string)
366371
login
367372
}
368373
isCrossRepository
374+
isDraft
369375
}
370376
}
371377
}
@@ -460,6 +466,7 @@ func PullRequestList(client *Client, vars map[string]interface{}, limit int) ([]
460466
login
461467
}
462468
isCrossRepository
469+
isDraft
463470
}
464471
`
465472

api/queries_repo.go

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ import (
66
"fmt"
77
"sort"
88
"strings"
9+
"time"
910

1011
"github.com/cli/cli/internal/ghrepo"
1112
)
1213

1314
// Repository contains information about a GitHub repo
1415
type Repository struct {
15-
ID string
16-
Name string
17-
URL string
18-
Owner RepositoryOwner
16+
ID string
17+
Name string
18+
URL string
19+
CloneURL string
20+
CreatedAt time.Time
21+
Owner RepositoryOwner
1922

2023
IsPrivate bool
2124
HasIssuesEnabled bool
@@ -60,7 +63,6 @@ func (r Repository) ViewerCanPush() bool {
6063
}
6164
}
6265

63-
// GitHubRepo looks up the node ID of a named repository
6466
func GitHubRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
6567
query := `
6668
query($owner: String!, $name: String!) {
@@ -79,12 +81,8 @@ func GitHubRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
7981
}{}
8082
err := client.GraphQL(query, variables, &result)
8183

82-
if err != nil || result.Repository.ID == "" {
83-
newErr := fmt.Errorf("failed to determine repository ID for '%s'", ghrepo.FullName(repo))
84-
if err != nil {
85-
newErr = fmt.Errorf("%s: %w", newErr, err)
86-
}
87-
return nil, newErr
84+
if err != nil {
85+
return nil, err
8886
}
8987

9088
return &result.Repository, nil
@@ -98,7 +96,7 @@ type RepoNetworkResult struct {
9896

9997
// RepoNetwork inspects the relationship between multiple GitHub repositories
10098
func RepoNetwork(client *Client, repos []ghrepo.Interface) (RepoNetworkResult, error) {
101-
queries := []string{}
99+
queries := make([]string, 0, len(repos))
102100
for i, repo := range repos {
103101
queries = append(queries, fmt.Sprintf(`
104102
repo_%03d: repository(owner: %q, name: %q) {
@@ -113,8 +111,8 @@ func RepoNetwork(client *Client, repos []ghrepo.Interface) (RepoNetworkResult, e
113111
// Since the query is constructed dynamically, we can't parse a response
114112
// format using a static struct. Instead, hold the raw JSON data until we
115113
// decide how to parse it manually.
116-
graphqlResult := map[string]*json.RawMessage{}
117-
result := RepoNetworkResult{}
114+
graphqlResult := make(map[string]*json.RawMessage)
115+
var result RepoNetworkResult
118116

119117
err := client.GraphQL(fmt.Sprintf(`
120118
fragment repo on Repository {
@@ -151,7 +149,7 @@ func RepoNetwork(client *Client, repos []ghrepo.Interface) (RepoNetworkResult, e
151149
return result, err
152150
}
153151

154-
keys := []string{}
152+
keys := make([]string, 0, len(graphqlResult))
155153
for key := range graphqlResult {
156154
keys = append(keys, key)
157155
}
@@ -176,8 +174,8 @@ func RepoNetwork(client *Client, repos []ghrepo.Interface) (RepoNetworkResult, e
176174
result.Repositories = append(result.Repositories, nil)
177175
continue
178176
}
179-
repo := Repository{}
180-
decoder := json.NewDecoder(bytes.NewReader([]byte(*jsonMessage)))
177+
var repo Repository
178+
decoder := json.NewDecoder(bytes.NewReader(*jsonMessage))
181179
if err := decoder.Decode(&repo); err != nil {
182180
return result, err
183181
}
@@ -191,9 +189,11 @@ func RepoNetwork(client *Client, repos []ghrepo.Interface) (RepoNetworkResult, e
191189

192190
// repositoryV3 is the repository result from GitHub API v3
193191
type repositoryV3 struct {
194-
NodeID string
195-
Name string
196-
Owner struct {
192+
NodeID string
193+
Name string
194+
CreatedAt time.Time `json:"created_at"`
195+
CloneURL string `json:"clone_url"`
196+
Owner struct {
197197
Login string
198198
}
199199
}
@@ -209,8 +209,10 @@ func ForkRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
209209
}
210210

211211
return &Repository{
212-
ID: result.NodeID,
213-
Name: result.Name,
212+
ID: result.NodeID,
213+
Name: result.Name,
214+
CloneURL: result.CloneURL,
215+
CreatedAt: result.CreatedAt,
214216
Owner: RepositoryOwner{
215217
Login: result.Owner.Login,
216218
},

auth/oauth.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ func (oa *OAuthFlow) ObtainAccessToken() (accessToken string, err error) {
5656
oa.logf("open %s\n", startURL)
5757
if err := openInBrowser(startURL); err != nil {
5858
fmt.Fprintf(os.Stderr, "error opening web browser: %s\n", err)
59+
fmt.Fprintf(os.Stderr, "")
5960
fmt.Fprintf(os.Stderr, "Please open the following URL manually:\n%s\n", startURL)
61+
fmt.Fprintf(os.Stderr, "")
62+
// TODO: Temporary workaround for https://github.com/cli/cli/issues/297
63+
fmt.Fprintf(os.Stderr, "If you are on a server or other headless system, use this workaround instead:")
64+
fmt.Fprintf(os.Stderr, " 1. Complete authentication on a GUI system")
65+
fmt.Fprintf(os.Stderr, " 2. Copy the contents of ~/.config/gh/config.yml to this system")
6066
}
6167

6268
http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

command/issue.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ var issueViewCmd = &cobra.Command{
6969
Use: "view {<number> | <url> | <branch>}",
7070
Args: func(cmd *cobra.Command, args []string) error {
7171
if len(args) < 1 {
72-
return FlagError{errors.New("issue required as argument")}
72+
return FlagError{errors.New("issue number or URL required as argument")}
7373
}
7474
return nil
7575
},
@@ -273,7 +273,7 @@ func printIssuePreview(out io.Writer, issue *api.Issue) error {
273273
var issueURLRE = regexp.MustCompile(`^https://github\.com/([^/]+)/([^/]+)/issues/(\d+)`)
274274

275275
func issueFromArg(apiClient *api.Client, baseRepo ghrepo.Interface, arg string) (*api.Issue, error) {
276-
if issueNumber, err := strconv.Atoi(arg); err == nil {
276+
if issueNumber, err := strconv.Atoi(strings.TrimPrefix(arg, "#")); err == nil {
277277
return api.IssueByNumber(apiClient, baseRepo, issueNumber)
278278
}
279279

@@ -429,7 +429,7 @@ func labelList(issue api.Issue) string {
429429
return ""
430430
}
431431

432-
labelNames := []string{}
432+
labelNames := make([]string, 0, len(issue.Labels.Nodes))
433433
for _, label := range issue.Labels.Nodes {
434434
labelNames = append(labelNames, label.Name)
435435
}

command/issue_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,40 @@ func TestIssueView(t *testing.T) {
243243
eq(t, url, "https://github.com/OWNER/REPO/issues/123")
244244
}
245245

246+
func TestIssueView_numberArgWithHash(t *testing.T) {
247+
initBlankContext("OWNER/REPO", "master")
248+
http := initFakeHTTP()
249+
http.StubRepoResponse("OWNER", "REPO")
250+
251+
http.StubResponse(200, bytes.NewBufferString(`
252+
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
253+
"number": 123,
254+
"url": "https://github.com/OWNER/REPO/issues/123"
255+
} } } }
256+
`))
257+
258+
var seenCmd *exec.Cmd
259+
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
260+
seenCmd = cmd
261+
return &outputStub{}
262+
})
263+
defer restoreCmd()
264+
265+
output, err := RunCommand(issueViewCmd, "issue view \"#123\"")
266+
if err != nil {
267+
t.Errorf("error running command `issue view`: %v", err)
268+
}
269+
270+
eq(t, output.String(), "")
271+
eq(t, output.Stderr(), "Opening https://github.com/OWNER/REPO/issues/123 in your browser.\n")
272+
273+
if seenCmd == nil {
274+
t.Fatal("expected a command to run")
275+
}
276+
url := seenCmd.Args[len(seenCmd.Args)-1]
277+
eq(t, url, "https://github.com/OWNER/REPO/issues/123")
278+
}
279+
246280
func TestIssueView_preview(t *testing.T) {
247281
initBlankContext("OWNER/REPO", "master")
248282
http := initFakeHTTP()

0 commit comments

Comments
 (0)