Skip to content

Commit e3cc911

Browse files
committed
Merge remote-tracking branch 'origin/master' into revert-184-release-notes
2 parents 2fa70eb + bce5d21 commit e3cc911

File tree

7 files changed

+473
-23
lines changed

7 files changed

+473
-23
lines changed

.github/workflows/go.yml

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,63 @@
1-
name: Go
1+
name: Tests
22
on: [push]
33
jobs:
4-
5-
build:
6-
name: Build
4+
build-linux:
5+
name: Linux Build
76
runs-on: ubuntu-latest
87
steps:
8+
- name: Set up Go 1.13
9+
uses: actions/setup-go@v1
10+
with:
11+
go-version: 1.13
12+
id: go
13+
14+
- name: Check out code into the Go module directory
15+
uses: actions/checkout@v1
16+
17+
- name: Verify dependencies
18+
run: go mod verify
19+
20+
- name: Build
21+
run: |
22+
go test ./...
23+
go build -v .
24+
build-windows:
25+
name: Windows Build
26+
runs-on: windows-latest
27+
steps:
28+
- name: Set up Go 1.13
29+
uses: actions/setup-go@v1
30+
with:
31+
go-version: 1.13
32+
id: go
33+
34+
- name: Check out code into the Go module directory
35+
uses: actions/checkout@v1
936

10-
- name: Set up Go 1.13
11-
uses: actions/setup-go@v1
12-
with:
13-
go-version: 1.13
14-
id: go
37+
- name: Verify dependencies
38+
run: go mod verify
39+
40+
- name: Build
41+
run: |
42+
go test ./...
43+
go build -v .
44+
build-macos:
45+
name: MacOS Build
46+
runs-on: macos-latest
47+
steps:
48+
- name: Set up Go 1.13
49+
uses: actions/setup-go@v1
50+
with:
51+
go-version: 1.13
52+
id: go
1553

16-
- name: Check out code into the Go module directory
17-
uses: actions/checkout@v1
54+
- name: Check out code into the Go module directory
55+
uses: actions/checkout@v1
1856

19-
- name: Verify dependencies
20-
run: go mod verify
57+
- name: Verify dependencies
58+
run: go mod verify
2159

22-
- name: Build
23-
run: |
24-
go test ./...
25-
go build -v .
60+
- name: Build
61+
run: |
62+
go test ./...
63+
go build -v .

command/issue.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package command
33
import (
44
"fmt"
55
"io"
6-
"os"
76
"regexp"
87
"strconv"
98
"strings"
109

1110
"github.com/github/gh-cli/api"
1211
"github.com/github/gh-cli/context"
12+
"github.com/github/gh-cli/git"
13+
"github.com/github/gh-cli/pkg/githubtemplate"
1314
"github.com/github/gh-cli/utils"
1415
"github.com/pkg/errors"
1516
"github.com/spf13/cobra"
@@ -241,11 +242,16 @@ func issueCreate(cmd *cobra.Command, args []string) error {
241242
return err
242243
}
243244

245+
var templateFiles []string
246+
if rootDir, err := git.ToplevelDir(); err == nil {
247+
// TODO: figure out how to stub this in tests
248+
templateFiles = githubtemplate.Find(rootDir, "ISSUE_TEMPLATE")
249+
}
250+
244251
if isWeb, err := cmd.Flags().GetBool("web"); err == nil && isWeb {
245252
// TODO: move URL generation into GitHubRepository
246253
openURL := fmt.Sprintf("https://github.com/%s/%s/issues/new", baseRepo.RepoOwner(), baseRepo.RepoName())
247-
// TODO: figure out how to stub this in tests
248-
if stat, err := os.Stat(".github/ISSUE_TEMPLATE"); err == nil && stat.IsDir() {
254+
if len(templateFiles) > 1 {
249255
openURL += "/choose"
250256
}
251257
cmd.Printf("Opening %s in your browser.\n", openURL)
@@ -277,7 +283,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
277283
interactive := title == "" || body == ""
278284

279285
if interactive {
280-
tb, err := titleBodySurvey(cmd, title, body)
286+
tb, err := titleBodySurvey(cmd, title, body, templateFiles)
281287
if err != nil {
282288
return errors.Wrap(err, "could not collect title and/or body")
283289
}

command/pr_create.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/github/gh-cli/api"
99
"github.com/github/gh-cli/context"
1010
"github.com/github/gh-cli/git"
11+
"github.com/github/gh-cli/pkg/githubtemplate"
1112
"github.com/github/gh-cli/utils"
1213
"github.com/pkg/errors"
1314
"github.com/spf13/cobra"
@@ -71,7 +72,13 @@ func prCreate(cmd *cobra.Command, _ []string) error {
7172
interactive := title == "" || body == ""
7273

7374
if interactive {
74-
tb, err := titleBodySurvey(cmd, title, body)
75+
var templateFiles []string
76+
if rootDir, err := git.ToplevelDir(); err == nil {
77+
// TODO: figure out how to stub this in tests
78+
templateFiles = githubtemplate.Find(rootDir, "PULL_REQUEST_TEMPLATE")
79+
}
80+
81+
tb, err := titleBodySurvey(cmd, title, body, templateFiles)
7582
if err != nil {
7683
return errors.Wrap(err, "could not collect title and/or body")
7784
}

command/title_body_survey.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package command
22

33
import (
44
"fmt"
5+
56
"github.com/AlecAivazis/survey/v2"
7+
"github.com/github/gh-cli/pkg/githubtemplate"
68
"github.com/pkg/errors"
79
"github.com/spf13/cobra"
810
)
@@ -44,9 +46,45 @@ func confirm() (int, error) {
4446
return confirmAnswers.Confirmation, nil
4547
}
4648

47-
func titleBodySurvey(cmd *cobra.Command, providedTitle string, providedBody string) (*titleBody, error) {
49+
func selectTemplate(templatePaths []string) (string, error) {
50+
templateResponse := struct {
51+
Index int
52+
}{}
53+
if len(templatePaths) > 1 {
54+
templateNames := []string{}
55+
for _, p := range templatePaths {
56+
templateNames = append(templateNames, githubtemplate.ExtractName(p))
57+
}
58+
59+
selectQs := []*survey.Question{
60+
{
61+
Name: "index",
62+
Prompt: &survey.Select{
63+
Message: "Choose a template",
64+
Options: templateNames,
65+
},
66+
},
67+
}
68+
if err := survey.Ask(selectQs, &templateResponse); err != nil {
69+
return "", errors.Wrap(err, "could not prompt")
70+
}
71+
}
72+
73+
templateContents := githubtemplate.ExtractContents(templatePaths[templateResponse.Index])
74+
return string(templateContents), nil
75+
}
76+
77+
func titleBodySurvey(cmd *cobra.Command, providedTitle string, providedBody string, templatePaths []string) (*titleBody, error) {
4878
inProgress := titleBody{}
4979

80+
if providedBody == "" && len(templatePaths) > 0 {
81+
templateContents, err := selectTemplate(templatePaths)
82+
if err != nil {
83+
return nil, err
84+
}
85+
inProgress.Body = templateContents
86+
}
87+
5088
confirmed := false
5189
editor := determineEditor()
5290

@@ -64,6 +102,7 @@ func titleBodySurvey(cmd *cobra.Command, providedTitle string, providedBody stri
64102
Message: fmt.Sprintf("Body (%s)", editor),
65103
FileName: "*.md",
66104
Default: inProgress.Body,
105+
HideDefault: true,
67106
AppendDefault: true,
68107
Editor: editor,
69108
},

git/git.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ func ReadBranchConfig(branch string) (cfg BranchConfig) {
111111
return
112112
}
113113

114+
// ToplevelDir returns the top-level directory path of the current repository
115+
func ToplevelDir() (string, error) {
116+
showCmd := exec.Command("git", "rev-parse", "--show-toplevel")
117+
output, err := utils.PrepareCmd(showCmd).Output()
118+
return firstLine(output), err
119+
120+
}
121+
114122
func outputLines(output []byte) []string {
115123
lines := strings.TrimSuffix(string(output), "\n")
116124
return strings.Split(lines, "\n")
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package githubtemplate
2+
3+
import (
4+
"io/ioutil"
5+
"path"
6+
"regexp"
7+
"sort"
8+
"strings"
9+
10+
"gopkg.in/yaml.v3"
11+
)
12+
13+
// Find returns the list of template file paths
14+
func Find(rootDir string, name string) []string {
15+
results := []string{}
16+
17+
// https://help.github.com/en/github/building-a-strong-community/creating-a-pull-request-template-for-your-repository
18+
candidateDirs := []string{
19+
path.Join(rootDir, ".github"),
20+
rootDir,
21+
path.Join(rootDir, "docs"),
22+
}
23+
24+
mainLoop:
25+
for _, dir := range candidateDirs {
26+
files, err := ioutil.ReadDir(dir)
27+
if err != nil {
28+
continue
29+
}
30+
31+
// detect multiple templates in a subdirectory
32+
for _, file := range files {
33+
if strings.EqualFold(file.Name(), name) && file.IsDir() {
34+
templates, err := ioutil.ReadDir(path.Join(dir, file.Name()))
35+
if err != nil {
36+
break
37+
}
38+
for _, tf := range templates {
39+
if strings.HasSuffix(tf.Name(), ".md") {
40+
results = append(results, path.Join(dir, file.Name(), tf.Name()))
41+
}
42+
}
43+
if len(results) > 0 {
44+
break mainLoop
45+
}
46+
break
47+
}
48+
}
49+
50+
// detect a single template file
51+
for _, file := range files {
52+
if strings.EqualFold(file.Name(), name+".md") {
53+
results = append(results, path.Join(dir, file.Name()))
54+
break
55+
}
56+
}
57+
if len(results) > 0 {
58+
break
59+
}
60+
}
61+
62+
sort.Sort(sort.StringSlice(results))
63+
return results
64+
}
65+
66+
// ExtractName returns the name of the template from YAML front-matter
67+
func ExtractName(filePath string) string {
68+
contents, err := ioutil.ReadFile(filePath)
69+
if err == nil && detectFrontmatter(contents)[0] == 0 {
70+
templateData := struct {
71+
Name string
72+
}{}
73+
if err := yaml.Unmarshal(contents, &templateData); err == nil && templateData.Name != "" {
74+
return templateData.Name
75+
}
76+
}
77+
return path.Base(filePath)
78+
}
79+
80+
// ExtractContents returns the template contents without the YAML front-matter
81+
func ExtractContents(filePath string) []byte {
82+
contents, err := ioutil.ReadFile(filePath)
83+
if err != nil {
84+
return []byte{}
85+
}
86+
if frontmatterBoundaries := detectFrontmatter(contents); frontmatterBoundaries[0] == 0 {
87+
return contents[frontmatterBoundaries[1]:]
88+
}
89+
return contents
90+
}
91+
92+
var yamlPattern = regexp.MustCompile(`(?m)^---\r?\n(\s*\r?\n)?`)
93+
94+
func detectFrontmatter(c []byte) []int {
95+
if matches := yamlPattern.FindAllIndex(c, 2); len(matches) > 1 {
96+
return []int{matches[0][0], matches[1][1]}
97+
}
98+
return []int{-1, -1}
99+
}

0 commit comments

Comments
 (0)