diff --git a/app/services/merge_requests/push_options_handler_service.rb b/app/services/merge_requests/push_options_handler_service.rb index cc1e08e16062ef0e6035e30f921e0fd4fbb956b1..ad0b033f082369a329a642761005d51ac6cb4e1b 100644 --- a/app/services/merge_requests/push_options_handler_service.rb +++ b/app/services/merge_requests/push_options_handler_service.rb @@ -142,6 +142,8 @@ def base_params params[:add_assignee_ids] = params.delete(:assign).keys if params.has_key?(:assign) params[:remove_assignee_ids] = params.delete(:unassign).keys if params.has_key?(:unassign) + params[:milestone] = project.milestones&.find_by_name(push_options[:milestone]) if push_options[:milestone] + params end diff --git a/doc/user/project/push_options.md b/doc/user/project/push_options.md index 728c682b0099884e58e3d98133c9f155ad45a80e..46619c96f2f1ce247c003a73320bc05ac0fbf58f 100644 --- a/doc/user/project/push_options.md +++ b/doc/user/project/push_options.md @@ -66,6 +66,7 @@ time as pushing changes: | `merge_request.remove_source_branch` | Set the merge request to remove the source branch when it's merged. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) | | `merge_request.title="<title>"` | Set the title of the merge request. Ex: `git push -o merge_request.title="The title I want"`. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) | | `merge_request.description="<description>"` | Set the description of the merge request. Ex: `git push -o merge_request.description="The description I want"`. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) | +| `merge_request.milestone="<milestone>"` | Set the milestone of the merge request. Ex: `git push -o merge_request.milestone="3.0"`. | [14.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63960) | | `merge_request.label="<label>"` | Add labels to the merge request. If the label does not exist, it is created. For example, for two labels: `git push -o merge_request.label="label1" -o merge_request.label="label2"`. | [12.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31831) | | `merge_request.unlabel="<label>"` | Remove labels from the merge request. For example, for two labels: `git push -o merge_request.unlabel="label1" -o merge_request.unlabel="label2"`. | [12.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31831) | | `merge_request.assign="<user>"` | Assign users to the merge request. For example, for two users: `git push -o merge_request.assign="user1" -o merge_request.assign="user2"`. | [13.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25904) | diff --git a/lib/gitlab/push_options.rb b/lib/gitlab/push_options.rb index ce9fced946550684eb8a4e85f42b2971ceee2948..9d954a74948d874237da4186abef7739f6dab579 100644 --- a/lib/gitlab/push_options.rb +++ b/lib/gitlab/push_options.rb @@ -10,6 +10,7 @@ class PushOptions :description, :label, :merge_when_pipeline_succeeds, + :milestone, :remove_source_branch, :target, :title, diff --git a/spec/services/merge_requests/push_options_handler_service_spec.rb b/spec/services/merge_requests/push_options_handler_service_spec.rb index 87c3fc6a2d8a105f93531cefb2edef38aa2e7e84..c09435a70e221810858aa1b354a3c594430d5a60 100644 --- a/spec/services/merge_requests/push_options_handler_service_spec.rb +++ b/spec/services/merge_requests/push_options_handler_service_spec.rb @@ -10,6 +10,7 @@ let_it_be(:user2) { create(:user, developer_projects: [project]) } let_it_be(:user3) { create(:user, developer_projects: [project]) } let_it_be(:forked_project) { fork_project(project, user1, repository: true) } + let_it_be(:milestone) { create(:milestone, project: project, title: '1.0') } let(:service) { described_class.new(project: project, current_user: user1, changes: changes, push_options: push_options) } let(:source_branch) { 'fix' } @@ -59,6 +60,16 @@ end end + shared_examples_for 'a service that can set the milestone of a merge request' do + subject(:last_mr) { MergeRequest.last } + + it 'sets the milestone' do + service.execute + + expect(last_mr.milestone&.title).to eq(expected_milestone) + end + end + shared_examples_for 'a service that can set the merge request to merge when pipeline succeeds' do subject(:last_mr) { MergeRequest.last } @@ -514,6 +525,70 @@ it_behaves_like 'with the project default branch' end + describe '`milestone` push option' do + context 'with a valid milestone' do + let(:expected_milestone) { milestone.title } + let(:push_options) { { milestone: milestone.title } } + + context 'with a new branch' do + let(:changes) { new_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + service.execute + + expect(service.errors).to include(error_mr_required) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, milestone: milestone.title } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the milestone of a merge request' + end + end + + context 'with an existing branch but no open MR' do + let(:changes) { existing_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + service.execute + + expect(service.errors).to include(error_mr_required) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, milestone: milestone.title } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the milestone of a merge request' + end + end + + context 'with an existing branch that has a merge request open' do + let(:changes) { existing_branch_changes } + let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)} + + it_behaves_like 'a service that does not create a merge request' + it_behaves_like 'a service that can set the milestone of a merge request' + end + + it_behaves_like 'with a deleted branch' + it_behaves_like 'with the project default branch' + end + + context 'with invalid milestone' do + let(:expected_milestone) { nil } + let(:changes) { new_branch_changes } + let(:push_options) { { create: true, milestone: 'invalid_milestone' } } + + it_behaves_like 'a service that can set the milestone of a merge request' + end + end + shared_examples 'with an existing branch that has a merge request open in foss' do let(:changes) { existing_branch_changes } let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)}