mirror of https://gitlab.com/gitlab-org/cli.git
Merge branch 'commits-in-template' into 'main'
Fill mr description from commits in editor Closes #1070 See merge request https://gitlab.com/gitlab-org/cli/-/merge_requests/1277 Merged-by: Gary Holtz <gholtz@gitlab.com> Approved-by: Gary Holtz <gholtz@gitlab.com> Reviewed-by: Benedek Thaler <benedek.thaler@wincent.co> Reviewed-by: Davis Bickford <dbickford@gitlab.com> Co-authored-by: Benedek Thaler <thaler@thaler.hu>
This commit is contained in:
commit
8aa562b422
|
@ -387,7 +387,11 @@ func createRun(opts *CreateOpts) error {
|
|||
return fmt.Errorf("error getting templates: %w", err)
|
||||
}
|
||||
|
||||
templateNames = append(templateNames, "Open a blank merge request")
|
||||
const mrWithCommitsTemplate = "Open a merge request with commit messages"
|
||||
const mrEmptyTemplate = "Open a blank merge request"
|
||||
|
||||
templateNames = append(templateNames, mrWithCommitsTemplate)
|
||||
templateNames = append(templateNames, mrEmptyTemplate)
|
||||
|
||||
selectQs := []*survey.Question{
|
||||
{
|
||||
|
@ -402,8 +406,21 @@ func createRun(opts *CreateOpts) error {
|
|||
if err := prompt.Ask(selectQs, &templateResponse); err != nil {
|
||||
return fmt.Errorf("could not prompt: %w", err)
|
||||
}
|
||||
if templateResponse.Index != len(templateNames) {
|
||||
templateName = templateNames[templateResponse.Index]
|
||||
|
||||
templateName = templateNames[templateResponse.Index]
|
||||
if templateName == mrWithCommitsTemplate {
|
||||
// templateContents should be filled from commit messages
|
||||
commits, err := git.Commits(opts.TargetTrackingBranch, opts.SourceBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get commits: %w", err)
|
||||
}
|
||||
templateContents, err = mrBody(commits, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if templateName == mrEmptyTemplate {
|
||||
// blank merge request was choosen, leave templateContents empty
|
||||
} else {
|
||||
templateContents, err = cmdutils.LoadGitLabTemplate(cmdutils.MergeRequestTemplate, templateName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get template contents: %w", err)
|
||||
|
@ -617,6 +634,27 @@ func createRun(opts *CreateOpts) error {
|
|||
return errors.New("expected to cancel, preview in browser, or submit")
|
||||
}
|
||||
|
||||
func mrBody(commits []*git.Commit, fillCommitBody bool) (string, error) {
|
||||
var body strings.Builder
|
||||
re := regexp.MustCompile(`\r?\n\n`)
|
||||
|
||||
for i := len(commits) - 1; i >= 0; i-- {
|
||||
// adds 2 spaces for markdown line wrapping
|
||||
fmt.Fprintf(&body, "- %s \n", commits[i].Title)
|
||||
|
||||
if fillCommitBody {
|
||||
commitBody, err := git.CommitBody(commits[i].Sha)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get commit message for %s: %w", commits[i].Sha, err)
|
||||
}
|
||||
commitBody = re.ReplaceAllString(commitBody, " \n")
|
||||
fmt.Fprintf(&body, "%s\n", commitBody)
|
||||
}
|
||||
}
|
||||
|
||||
return body.String(), nil
|
||||
}
|
||||
|
||||
func mrBodyAndTitle(opts *CreateOpts) error {
|
||||
// TODO: detect forks
|
||||
commits, err := git.Commits(opts.TargetTrackingBranch, opts.SourceBranch)
|
||||
|
@ -640,22 +678,11 @@ func mrBodyAndTitle(opts *CreateOpts) error {
|
|||
}
|
||||
|
||||
if opts.Description == "" {
|
||||
var body strings.Builder
|
||||
for i := len(commits) - 1; i >= 0; i-- {
|
||||
// adds 2 spaces for markdown line wrapping
|
||||
fmt.Fprintf(&body, "- %s \n", commits[i].Title)
|
||||
|
||||
if opts.FillCommitBody {
|
||||
commitBody, err := git.CommitBody(commits[i].Sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re := regexp.MustCompile(`\r?\n\n`)
|
||||
commitBody = re.ReplaceAllString(commitBody, " \n")
|
||||
fmt.Fprintf(&body, "%s\n", commitBody)
|
||||
}
|
||||
description, err := mrBody(commits, opts.FillCommitBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Description = body.String()
|
||||
opts.Description = description
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -235,6 +235,99 @@ func TestNewCmdCreate_RelatedIssue(t *testing.T) {
|
|||
assert.Contains(t, output.String(), "https://gitlab.com/OWNER/REPO/-/merge_requests/12")
|
||||
}
|
||||
|
||||
func TestNewCmdCreate_TemplateFromCommitMessages(t *testing.T) {
|
||||
fakeHTTP := httpmock.New()
|
||||
defer fakeHTTP.Verify(t)
|
||||
|
||||
fakeHTTP.RegisterResponder(http.MethodPost, "/projects/OWNER/REPO/merge_requests",
|
||||
func(req *http.Request) (*http.Response, error) {
|
||||
rb, _ := io.ReadAll(req.Body)
|
||||
assert.Contains(t, string(rb), "- commit msg 1 \\n\\n")
|
||||
assert.Contains(t, string(rb), "- commit msg 2 \\ncommit body")
|
||||
resp, _ := httpmock.NewStringResponse(http.StatusCreated, `
|
||||
{
|
||||
"id": 1,
|
||||
"iid": 12,
|
||||
"project_id": 3,
|
||||
"title": "...",
|
||||
"description": "...",
|
||||
"state": "opened",
|
||||
"target_branch": "master",
|
||||
"source_branch": "feat-new-mr",
|
||||
"web_url": "https://gitlab.com/OWNER/REPO/-/merge_requests/12"
|
||||
}
|
||||
`)(req)
|
||||
return resp, nil
|
||||
},
|
||||
)
|
||||
fakeHTTP.RegisterResponder(http.MethodGet, "/projects/OWNER/REPO",
|
||||
httpmock.NewStringResponse(http.StatusOK, `
|
||||
{
|
||||
"id": 1,
|
||||
"description": null,
|
||||
"default_branch": "master",
|
||||
"web_url": "http://gitlab.com/OWNER/REPO",
|
||||
"name": "OWNER",
|
||||
"path": "REPO",
|
||||
"merge_requests_enabled": true,
|
||||
"path_with_namespace": "OWNER/REPO"
|
||||
}
|
||||
`),
|
||||
)
|
||||
|
||||
ask, teardown := prompt.InitAskStubber()
|
||||
defer teardown()
|
||||
|
||||
ask.Stub([]*prompt.QuestionStub{
|
||||
{
|
||||
Name: "index",
|
||||
Value: 0,
|
||||
},
|
||||
})
|
||||
ask.Stub([]*prompt.QuestionStub{
|
||||
{
|
||||
Name: "Description",
|
||||
Default: true,
|
||||
},
|
||||
})
|
||||
|
||||
cs, csTeardown := test.InitCmdStubber()
|
||||
defer csTeardown()
|
||||
|
||||
cs.Stub("HEAD branch: main\n") // git remote show <name>
|
||||
cs.Stub("/") // git rev-parse --show-toplevel
|
||||
|
||||
// git -c log.ShowSignature=false log --pretty=format:%H,%s --cherry upstream/main...feat-new-mr
|
||||
cs.Stub(heredoc.Doc(`
|
||||
deadb00f,commit msg 2
|
||||
deadbeef,commit msg 1
|
||||
`))
|
||||
|
||||
// git -c log.ShowSignature=false show -s --pretty=format:%b deadbeef
|
||||
cs.Stub("")
|
||||
// git -c log.ShowSignature=false show -s --pretty=format:%b deadb00f
|
||||
cs.Stub("commit body")
|
||||
|
||||
cliStr := []string{
|
||||
"--source-branch", "feat-new-mr",
|
||||
"--title", "mr-title",
|
||||
"--yes",
|
||||
}
|
||||
|
||||
cli := strings.Join(cliStr, " ")
|
||||
|
||||
t.Log(cli)
|
||||
|
||||
output, err := runCommand(fakeHTTP, "feat-new-mr", true, cli)
|
||||
if err != nil {
|
||||
if errors.Is(err, cmdutils.SilentError) {
|
||||
t.Errorf("Unexpected error: %q", output.Stderr())
|
||||
}
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCmdCreate_RelatedIssueWithTitleAndDescription(t *testing.T) {
|
||||
fakeHTTP := httpmock.New()
|
||||
defer fakeHTTP.Verify(t)
|
||||
|
|
Loading…
Reference in New Issue