Skip to content

Commit 1ba577c

Browse files
committed
Merge remote-tracking branch 'origin/master' into pr-count
2 parents debedf9 + d4cb2d8 commit 1ba577c

File tree

18 files changed

+1122
-83
lines changed

18 files changed

+1122
-83
lines changed

.github/workflows/releases.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ jobs:
2626
args: release --release-notes=CHANGELOG.md
2727
env:
2828
GITHUB_TOKEN: ${{secrets.UPLOAD_GITHUB_TOKEN}}
29+
- name: move cards
30+
if: "!contains(github.ref, '-')"
31+
env:
32+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
33+
PENDING_COLUMN: 8189733
34+
DONE_COLUMN: 7110130
35+
shell: bash
36+
run: |
37+
curl -fsSL https://github.com/github/hub/raw/master/script/get | bash -s 2.14.1
38+
api() { bin/hub api -H 'accept: application/vnd.github.inertia-preview+json' "$@"; }
39+
cards=$(api projects/columns/$PENDING_COLUMN/cards | jq ".[].id")
40+
for card in $cards; do api projects/columns/cards/$card/moves --field position=top --field column_id=$DONE_COLUMN; done
2941
msi:
3042
needs: goreleaser
3143
runs-on: windows-latest

Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ test:
2323
.PHONY: test
2424

2525
site:
26-
git worktree add site gh-pages
26+
git clone https://github.com/github/cli.github.com.git "$@"
2727

2828
site-docs: site
2929
git -C site pull
@@ -33,5 +33,4 @@ site-docs: site
3333
rm -f site/manual/*.bak
3434
git -C site add 'manual/gh*.md'
3535
git -C site commit -m 'update help docs'
36-
git -C site push
3736
.PHONY: site-docs

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ And if you spot bugs or have features that you'd really like to see in `gh`, ple
2121

2222
- `gh pr [status, list, view, checkout, create]`
2323
- `gh issue [status, list, view, create]`
24+
- `gh repo [view, create, clone, fork]`
2425
- `gh help`
2526

2627
Check out the [docs][] for more information.

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_org.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package api
2+
3+
import "fmt"
4+
5+
// using API v3 here because the equivalent in GraphQL needs `read:org` scope
6+
func resolveOrganization(client *Client, orgName string) (string, error) {
7+
var response struct {
8+
NodeID string `json:"node_id"`
9+
}
10+
err := client.REST("GET", fmt.Sprintf("users/%s", orgName), nil, &response)
11+
return response.NodeID, err
12+
}
13+
14+
// using API v3 here because the equivalent in GraphQL needs `read:org` scope
15+
func resolveOrganizationTeam(client *Client, orgName, teamSlug string) (string, string, error) {
16+
var response struct {
17+
NodeID string `json:"node_id"`
18+
Organization struct {
19+
NodeID string `json:"node_id"`
20+
}
21+
}
22+
err := client.REST("GET", fmt.Sprintf("orgs/%s/teams/%s", orgName, teamSlug), nil, &response)
23+
return response.Organization.NodeID, response.NodeID, err
24+
}

api/queries_repo.go

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +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-
Owner RepositoryOwner
16+
ID string
17+
Name string
18+
URL string
19+
CloneURL string
20+
CreatedAt time.Time
21+
Owner RepositoryOwner
1822

1923
IsPrivate bool
2024
HasIssuesEnabled bool
@@ -59,7 +63,6 @@ func (r Repository) ViewerCanPush() bool {
5963
}
6064
}
6165

62-
// GitHubRepo looks up the node ID of a named repository
6366
func GitHubRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
6467
query := `
6568
query($owner: String!, $name: String!) {
@@ -78,12 +81,8 @@ func GitHubRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
7881
}{}
7982
err := client.GraphQL(query, variables, &result)
8083

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

8988
return &result.Repository, nil
@@ -190,9 +189,11 @@ func RepoNetwork(client *Client, repos []ghrepo.Interface) (RepoNetworkResult, e
190189

191190
// repositoryV3 is the repository result from GitHub API v3
192191
type repositoryV3 struct {
193-
NodeID string
194-
Name string
195-
Owner struct {
192+
NodeID string
193+
Name string
194+
CreatedAt time.Time `json:"created_at"`
195+
CloneURL string `json:"clone_url"`
196+
Owner struct {
196197
Login string
197198
}
198199
}
@@ -208,11 +209,73 @@ func ForkRepo(client *Client, repo ghrepo.Interface) (*Repository, error) {
208209
}
209210

210211
return &Repository{
211-
ID: result.NodeID,
212-
Name: result.Name,
212+
ID: result.NodeID,
213+
Name: result.Name,
214+
CloneURL: result.CloneURL,
215+
CreatedAt: result.CreatedAt,
213216
Owner: RepositoryOwner{
214217
Login: result.Owner.Login,
215218
},
216219
ViewerPermission: "WRITE",
217220
}, nil
218221
}
222+
223+
// RepoCreateInput represents input parameters for RepoCreate
224+
type RepoCreateInput struct {
225+
Name string `json:"name"`
226+
Visibility string `json:"visibility"`
227+
Homepage string `json:"homepage,omitempty"`
228+
Description string `json:"description,omitempty"`
229+
230+
OwnerID string `json:"ownerId,omitempty"`
231+
TeamID string `json:"teamId,omitempty"`
232+
233+
HasIssuesEnabled bool `json:"hasIssuesEnabled"`
234+
HasWikiEnabled bool `json:"hasWikiEnabled"`
235+
}
236+
237+
// RepoCreate creates a new GitHub repository
238+
func RepoCreate(client *Client, input RepoCreateInput) (*Repository, error) {
239+
var response struct {
240+
CreateRepository struct {
241+
Repository Repository
242+
}
243+
}
244+
245+
if input.TeamID != "" {
246+
orgID, teamID, err := resolveOrganizationTeam(client, input.OwnerID, input.TeamID)
247+
if err != nil {
248+
return nil, err
249+
}
250+
input.TeamID = teamID
251+
input.OwnerID = orgID
252+
} else if input.OwnerID != "" {
253+
orgID, err := resolveOrganization(client, input.OwnerID)
254+
if err != nil {
255+
return nil, err
256+
}
257+
input.OwnerID = orgID
258+
}
259+
260+
variables := map[string]interface{}{
261+
"input": input,
262+
}
263+
264+
err := client.GraphQL(`
265+
mutation($input: CreateRepositoryInput!) {
266+
createRepository(input: $input) {
267+
repository {
268+
id
269+
name
270+
owner { login }
271+
url
272+
}
273+
}
274+
}
275+
`, variables, &response)
276+
if err != nil {
277+
return nil, err
278+
}
279+
280+
return &response.CreateRepository.Repository, nil
281+
}

cmd/gen-docs/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func main() {
2828

2929
func filePrepender(filename string) string {
3030
return `---
31-
layout: page
31+
layout: manual
3232
permalink: /:path/:basename
3333
---
3434

command/pr.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,17 @@ func prStatus(cmd *cobra.Command, args []string) error {
7070
return err
7171
}
7272

73-
currentPRNumber, currentPRHeadRef, err := prSelectorForCurrentBranch(ctx)
73+
currentUser, err := ctx.AuthLogin()
7474
if err != nil {
7575
return err
7676
}
77-
currentUser, err := ctx.AuthLogin()
77+
78+
baseRepo, err := determineBaseRepo(cmd, ctx)
7879
if err != nil {
7980
return err
8081
}
8182

82-
baseRepo, err := determineBaseRepo(cmd, ctx)
83+
currentPRNumber, currentPRHeadRef, err := prSelectorForCurrentBranch(ctx, baseRepo)
8384
if err != nil {
8485
return err
8586
}
@@ -263,7 +264,7 @@ func prView(cmd *cobra.Command, args []string) error {
263264
}
264265
openURL = pr.URL
265266
} else {
266-
prNumber, branchWithOwner, err := prSelectorForCurrentBranch(ctx)
267+
prNumber, branchWithOwner, err := prSelectorForCurrentBranch(ctx, baseRepo)
267268
if err != nil {
268269
return err
269270
}
@@ -333,11 +334,7 @@ func prFromArg(apiClient *api.Client, baseRepo ghrepo.Interface, arg string) (*a
333334
return api.PullRequestForBranch(apiClient, baseRepo, arg)
334335
}
335336

336-
func prSelectorForCurrentBranch(ctx context.Context) (prNumber int, prHeadRef string, err error) {
337-
baseRepo, err := ctx.BaseRepo()
338-
if err != nil {
339-
return
340-
}
337+
func prSelectorForCurrentBranch(ctx context.Context, baseRepo ghrepo.Interface) (prNumber int, prHeadRef string, err error) {
341338
prHeadRef, err = ctx.Branch()
342339
if err != nil {
343340
return

command/pr_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,36 @@ func TestPRStatus(t *testing.T) {
9898
}
9999
}
100100

101+
func TestPRStatus_fork(t *testing.T) {
102+
initBlankContext("OWNER/REPO", "blueberries")
103+
http := initFakeHTTP()
104+
http.StubForkedRepoResponse("OWNER/REPO", "PARENT/REPO")
105+
106+
jsonFile, _ := os.Open("../test/fixtures/prStatusFork.json")
107+
defer jsonFile.Close()
108+
http.StubResponse(200, jsonFile)
109+
110+
defer utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
111+
switch strings.Join(cmd.Args, " ") {
112+
case `git config --get-regexp ^branch\.blueberries\.(remote|merge)$`:
113+
return &outputStub{[]byte(`branch.blueberries.remote origin
114+
branch.blueberries.merge refs/heads/blueberries`)}
115+
default:
116+
panic("not implemented")
117+
}
118+
})()
119+
120+
output, err := RunCommand(prStatusCmd, "pr status")
121+
if err != nil {
122+
t.Fatalf("error running command `pr status`: %v", err)
123+
}
124+
125+
branchRE := regexp.MustCompile(`#10.*\[OWNER:blueberries\]`)
126+
if !branchRE.MatchString(output.String()) {
127+
t.Errorf("did not match current branch:\n%v", output.String())
128+
}
129+
}
130+
101131
func TestPRStatus_reviewsAndChecks(t *testing.T) {
102132
initBlankContext("OWNER/REPO", "blueberries")
103133
http := initFakeHTTP()

0 commit comments

Comments
 (0)