Commit 6b0ea951 authored by Jacopo's avatar Jacopo

Creates /create_merge_request quickaction

With this quick action the user can create a new MR starting from
the current issue using as `source_branch` the given `branch name` and
as `target_branch` the project default branch. If the `branch name` is
omitted a name is automatically created starting from the issue title.
parent f5e3ce5e
...@@ -11,6 +11,12 @@ module Issues ...@@ -11,6 +11,12 @@ module Issues
move_issue_to_new_project(issue) || update(issue) move_issue_to_new_project(issue) || update(issue)
end end
def update(issue)
create_merge_request_from_quick_action
super
end
def before_update(issue) def before_update(issue)
spam_check(issue, current_user) spam_check(issue, current_user)
end end
...@@ -93,6 +99,13 @@ module Issues ...@@ -93,6 +99,13 @@ module Issues
private private
def create_merge_request_from_quick_action
create_merge_request_params = params.delete(:create_merge_request)
return unless create_merge_request_params
MergeRequests::CreateFromIssueService.new(project, current_user, create_merge_request_params).execute
end
def handle_milestone_change(issue) def handle_milestone_change(issue)
return if skip_milestone_email return if skip_milestone_email
......
...@@ -19,13 +19,15 @@ module MergeRequests ...@@ -19,13 +19,15 @@ module MergeRequests
result = CreateBranchService.new(project, current_user).execute(branch_name, ref) result = CreateBranchService.new(project, current_user).execute(branch_name, ref)
return result if result[:status] == :error return result if result[:status] == :error
SystemNoteService.new_issue_branch(issue, project, current_user, branch_name)
new_merge_request = create(merge_request) new_merge_request = create(merge_request)
if new_merge_request.valid? if new_merge_request.valid?
SystemNoteService.new_merge_request(issue, project, current_user, new_merge_request)
success(new_merge_request) success(new_merge_request)
else else
SystemNoteService.new_issue_branch(issue, project, current_user, branch_name)
error(new_merge_request.errors) error(new_merge_request.errors)
end end
end end
......
...@@ -635,6 +635,22 @@ module QuickActions ...@@ -635,6 +635,22 @@ module QuickActions
@updates[:tag_message] = message @updates[:tag_message] = message
end end
desc 'Create a merge request.'
explanation do |branch_name = nil|
branch_text = branch_name ? "branch '#{branch_name}'" : 'a branch'
"Creates #{branch_text} and a merge request to resolve this issue"
end
params "<branch name>"
condition do
issuable.is_a?(Issue) && current_user.can?(:create_merge_request_in, project) && current_user.can?(:push_code, project)
end
command :create_merge_request do |branch_name = nil|
@updates[:create_merge_request] = {
branch_name: branch_name,
issue_iid: issuable.iid
}
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def extract_users(params) def extract_users(params)
return [] if params.nil? return [] if params.nil?
......
...@@ -407,11 +407,17 @@ module SystemNoteService ...@@ -407,11 +407,17 @@ module SystemNoteService
def new_issue_branch(issue, project, author, branch) def new_issue_branch(issue, project, author, branch)
link = url_helpers.project_compare_url(project, from: project.default_branch, to: branch) link = url_helpers.project_compare_url(project, from: project.default_branch, to: branch)
body = "created branch [`#{branch}`](#{link})" body = "created branch [`#{branch}`](#{link}) to address this issue"
create_note(NoteSummary.new(issue, project, author, body, action: 'branch')) create_note(NoteSummary.new(issue, project, author, body, action: 'branch'))
end end
def new_merge_request(issue, project, author, merge_request)
body = "created merge request #{merge_request.to_reference} to address this issue"
create_note(NoteSummary.new(issue, project, author, body, action: 'merge'))
end
# Called when a Mentionable references a Noteable # Called when a Mentionable references a Noteable
# #
# noteable - Noteable object being referenced # noteable - Noteable object being referenced
......
---
title: Creates /create_merge_request quickaction
merge_request: 22485
author: Jacopo Beschi @jacopo-beschi
type: added
...@@ -53,6 +53,7 @@ discussions, and descriptions: ...@@ -53,6 +53,7 @@ discussions, and descriptions:
| `/target_branch <Local branch Name>` | Set target branch | | ✓ | | `/target_branch <Local branch Name>` | Set target branch | | ✓ |
| `/wip` | Toggle the Work In Progress status | | ✓ | | `/wip` | Toggle the Work In Progress status | | ✓ |
| `/merge` | Merge (when pipeline succeeds) | | ✓ | | `/merge` | Merge (when pipeline succeeds) | | ✓ |
| `/create_merge_request <branch name>` | Create a new merge request starting from the current issue | ✓ | |
## Quick actions for commit messages ## Quick actions for commit messages
......
...@@ -76,7 +76,7 @@ describe 'User creates branch and merge request on issue page', :js do ...@@ -76,7 +76,7 @@ describe 'User creates branch and merge request on issue page', :js do
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
expect(page).to have_content('created branch 1-cherry-coloured-funk') expect(page).to have_content("created merge request !1 to address this issue")
expect(page).to have_content('mentioned in merge request !1') expect(page).to have_content('mentioned in merge request !1')
end end
...@@ -106,7 +106,7 @@ describe 'User creates branch and merge request on issue page', :js do ...@@ -106,7 +106,7 @@ describe 'User creates branch and merge request on issue page', :js do
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
expect(page).to have_content('created branch custom-branch-name') expect(page).to have_content("created merge request !1 to address this issue")
expect(page).to have_content('mentioned in merge request !1') expect(page).to have_content('mentioned in merge request !1')
end end
......
...@@ -303,5 +303,63 @@ describe 'Issues > User uses quick actions', :js do ...@@ -303,5 +303,63 @@ describe 'Issues > User uses quick actions', :js do
end end
end end
end end
describe 'create a merge request starting from an issue' do
let(:project) { create(:project, :public, :repository) }
let(:issue) { create(:issue, project: project) }
def expect_mr_quickaction(success)
expect(page).to have_content 'Commands applied'
if success
expect(page).to have_content 'created merge request'
else
expect(page).not_to have_content 'created merge request'
end
end
it "doesn't create a merge request when the branch name is invalid" do
add_note("/create_merge_request invalid branch name")
wait_for_requests
expect_mr_quickaction(false)
end
it "doesn't create a merge request when a branch with that name already exists" do
add_note("/create_merge_request feature")
wait_for_requests
expect_mr_quickaction(false)
end
it 'creates a new merge request using issue iid and title as branch name when the branch name is empty' do
add_note("/create_merge_request")
wait_for_requests
expect_mr_quickaction(true)
created_mr = project.merge_requests.last
expect(created_mr.source_branch).to eq(issue.to_branch_name)
visit project_merge_request_path(project, created_mr)
expect(page).to have_content %{WIP: Resolve "#{issue.title}"}
end
it 'creates a merge request using the given branch name' do
branch_name = '1-feature'
add_note("/create_merge_request #{branch_name}")
expect_mr_quickaction(true)
created_mr = project.merge_requests.last
expect(created_mr.source_branch).to eq(branch_name)
visit project_merge_request_path(project, created_mr)
expect(page).to have_content %{WIP: Resolve "#{issue.title}"}
end
end
end end
end end
...@@ -61,7 +61,15 @@ describe MergeRequests::CreateFromIssueService do ...@@ -61,7 +61,15 @@ describe MergeRequests::CreateFromIssueService do
expect(project.repository.branch_exists?(custom_source_branch)).to be_truthy expect(project.repository.branch_exists?(custom_source_branch)).to be_truthy
end end
it 'creates a system note' do it 'creates the new_merge_request system note' do
expect(SystemNoteService).to receive(:new_merge_request).with(issue, project, user, instance_of(MergeRequest))
service.execute
end
it 'creates the new_issue_branch system note when the branch could be created but the merge_request cannot be created' do
project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
expect(SystemNoteService).to receive(:new_issue_branch).with(issue, project, user, issue.to_branch_name) expect(SystemNoteService).to receive(:new_issue_branch).with(issue, project, user, issue.to_branch_name)
service.execute service.execute
......
...@@ -1222,6 +1222,37 @@ describe QuickActions::InterpretService do ...@@ -1222,6 +1222,37 @@ describe QuickActions::InterpretService do
expect(commands).to be_empty expect(commands).to be_empty
expect(text).to eq("#{described_class::SHRUG}\n/close") expect(text).to eq("#{described_class::SHRUG}\n/close")
end end
context '/create_merge_request command' do
let(:branch_name) { '1-feature' }
let(:content) { "/create_merge_request #{branch_name}" }
let(:issuable) { issue }
context 'if issuable is not an Issue' do
let(:issuable) { merge_request }
it_behaves_like 'empty command'
end
context "when logged user cannot create_merge_requests in the project" do
let(:project) { create(:project, :archived) }
it_behaves_like 'empty command'
end
context 'when logged user cannot push code to the project' do
let(:project) { create(:project, :private) }
let(:service) { described_class.new(project, create(:user)) }
it_behaves_like 'empty command'
end
it 'populates create_merge_request with branch_name and issue iid' do
_, updates = service.execute(content, issuable)
expect(updates).to eq(create_merge_request: { branch_name: branch_name, issue_iid: issuable.iid })
end
end
end end
describe '#explain' do describe '#explain' do
...@@ -1473,5 +1504,27 @@ describe QuickActions::InterpretService do ...@@ -1473,5 +1504,27 @@ describe QuickActions::InterpretService do
end end
end end
end end
describe 'create a merge request' do
context 'with no branch name' do
let(:content) { '/create_merge_request' }
it 'uses the default branch name' do
_, explanations = service.explain(content, issue)
expect(explanations).to eq(['Creates a branch and a merge request to resolve this issue'])
end
end
context 'with a branch name' do
let(:content) { '/create_merge_request foo' }
it 'uses the given branch name' do
_, explanations = service.explain(content, issue)
expect(explanations).to eq(["Creates branch 'foo' and a merge request to resolve this issue"])
end
end
end
end end
end end
...@@ -432,6 +432,20 @@ describe SystemNoteService do ...@@ -432,6 +432,20 @@ describe SystemNoteService do
end end
end end
describe '.new_merge_request' do
subject { described_class.new_merge_request(noteable, project, author, merge_request) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
it_behaves_like 'a system note' do
let(:action) { 'merge' }
end
it 'sets the new merge request note text' do
expect(subject.note).to eq("created merge request #{merge_request.to_reference} to address this issue")
end
end
describe '.cross_reference' do describe '.cross_reference' do
subject { described_class.cross_reference(noteable, mentioner, author) } subject { described_class.cross_reference(noteable, mentioner, author) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment