Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/controllers/projects/merge_requests_controller.rb35
-rw-r--r--app/models/ci/commit.rb8
-rw-r--r--app/models/commit_status.rb58
-rw-r--r--app/models/merge_request.rb17
-rw-r--r--app/services/merge_requests/merge_service.rb16
-rw-r--r--app/services/merge_requests/merge_when_build_succeeds_service.rb43
-rw-r--r--app/services/merge_requests/refresh_service.rb6
-rw-r--r--app/services/system_note_service.rb14
-rw-r--r--app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml2
-rw-r--r--app/views/projects/merge_requests/merge.js.haml6
-rw-r--r--app/views/projects/merge_requests/widget/_open.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml23
-rw-r--r--app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml27
-rw-r--r--app/workers/merge_worker.rb13
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb7
-rw-r--r--db/schema.rb15
-rw-r--r--doc/api/merge_requests.md60
-rw-r--r--lib/api/merge_requests.rb68
-rw-r--r--spec/models/merge_request_spec.rb14
-rw-r--r--spec/services/system_note_service_spec.rb16
23 files changed, 363 insertions, 97 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 94f07a31689..08188324d74 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -28,6 +28,10 @@ v 8.2.0
- Allow to define cache in `.gitlab-ci.yml`
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
- Remove deprecated CI events from project settings page
+ - Use issue editor as cross reference comment author when issue is edited with a new mention.
+ - Merge when build succeeds (Zeger-Jan van de Weg)
+
+v 8.1.1
- [API] Add ability to fetch the commit ID of the last commit that actually touched a file
- Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
- Add "New file" link to dropdown on project page
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 08e4bcdf529..18f1ef604a4 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -19,6 +19,7 @@
.accept-merge-holder {
.accept-action {
display: inline-block;
+ float: left;
.accept_merge_request {
&.ci-pending,
@@ -37,14 +38,15 @@
.accept-control {
display: inline-block;
+ float: left;
margin: 0;
margin-left: 20px;
padding: 5px;
+ padding-top: 12px;
line-height: 20px;
&.right {
float: right;
- padding-top: 12px;
a {
color: $gl-gray;
}
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 188f0cc4cea..9db6ed5022d 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -2,7 +2,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :merge, :merge_check,
- :ci_status, :toggle_subscription
+ :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds
]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
before_action :validates_merge_request, only: [:show, :diffs, :commits]
@@ -150,15 +150,34 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
+ def cancel_merge_when_build_succeeds
+ unless @merge_request.can_be_merged_by?(current_user) || @merge_request.author == current_user
+ return access_denied!
+ end
+
+ if @merge_request.merge_when_build_succeeds?
+ @merge_request.reset_merge_when_build_succeeds
+ SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user)
+ end
+ end
+
def merge
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
- if @merge_request.mergeable?
- @merge_request.update(merge_error: nil)
- MergeWorker.perform_async(@merge_request.id, current_user.id, params)
- @status = true
+ unless @merge_request.mergeable?
+ @status = :failed
+ return
+ end
+
+ @merge_request.update(merge_error: nil)
+
+ if params[:merge_when_build_succeeds] && @merge_request.ci_commit.active?
+ MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
+ .execute(@merge_request)
+ @status = :merge_when_build_succeeds
else
- @status = false
+ MergeWorker.perform_async(@merge_request.id, current_user.id, params)
+ @status = :success
end
end
@@ -285,6 +304,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
permitted
end
+ def merge_params
+ params.permit(:should_remove_source_branch, :commit_message)
+ end
+
# Make sure merge requests created before 8.0
# have head file in refs/merge-requests/
def ensure_ref_fetched
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 33b57173928..f96b479feac 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -165,6 +165,14 @@ module Ci
status == 'canceled'
end
+ def active?
+ running? || pending?
+ end
+
+ def complete?
+ canceled? || success? || failed?
+ end
+
def duration
duration_array = latest_statuses.map(&:duration).compact
duration_array.reduce(:+).to_i
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index e70f4d37184..ff619965a57 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,34 +1,30 @@
# == Schema Information
#
-# Table name: ci_builds
-#
-# id :integer not null, primary key
-# project_id :integer
-# status :string(255)
-# finished_at :datetime
-# trace :text
-# created_at :datetime
-# updated_at :datetime
-# started_at :datetime
-# runner_id :integer
-# coverage :float
-# commit_id :integer
-# commands :text
-# job_id :integer
-# name :string(255)
-# deploy :boolean default(FALSE)
-# options :text
-# allow_failure :boolean default(FALSE), not null
-# stage :string(255)
-# trigger_request_id :integer
-# stage_idx :integer
-# tag :boolean
-# ref :string(255)
-# user_id :integer
-# type :string(255)
-# target_url :string(255)
-# description :string(255)
-# artifacts_file :text
+# project_id integer
+# status string
+# finished_at datetime
+# trace text
+# created_at datetime
+# updated_at datetime
+# started_at datetime
+# runner_id integer
+# coverage float
+# commit_id integer
+# commands text
+# job_id integer
+# name string
+# deploy boolean default: false
+# options text
+# allow_failure boolean default: false, null: false
+# stage string
+# trigger_request_id integer
+# stage_idx integer
+# tag boolean
+# ref string
+# user_id integer
+# type string
+# target_url string
+# description string
#
class CommitStatus < ActiveRecord::Base
@@ -79,6 +75,10 @@ class CommitStatus < ActiveRecord::Base
build.update_attributes finished_at: Time.now
end
+ after_transition [:pending, :running] => :success do |build, transition|
+ MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build)
+ end
+
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 2eb03b8ba5b..05c3bc074bb 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -35,9 +35,12 @@ class MergeRequest < ActiveRecord::Base
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
+ belongs_to :merge_user, class_name: "User"
has_one :merge_request_diff, dependent: :destroy
+ serialize :merge_params, Hash
+
after_create :create_merge_request_diff
after_update :update_merge_request_diff
@@ -251,6 +254,10 @@ class MergeRequest < ActiveRecord::Base
end
end
+ def mergeable_by_or_author(user)
+ self.can_be_merged_by?(user) || self.author == user
+ end
+
def mr_and_commit_notes
# Fetch comments only from last 100 commits
commits_for_notes_limit = 100
@@ -386,6 +393,16 @@ class MergeRequest < ActiveRecord::Base
message
end
+ def reset_merge_when_build_succeeds
+ return unless merge_when_build_succeeds?
+
+ self.merge_when_build_succeeds = false
+ self.merge_user = nil
+ self.merge_params = nil
+
+ self.save
+ end
+
# Return array of possible target branches
# depends on target project of MR
def target_branches
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 7963af127e1..db8d18a7d84 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -6,15 +6,12 @@ module MergeRequests
# Executed when you do merge via GitLab UI
#
class MergeService < MergeRequests::BaseService
- attr_reader :merge_request, :commit_message
+ attr_reader :merge_request
- def execute(merge_request, commit_message)
- @commit_message = commit_message
+ def execute(merge_request)
@merge_request = merge_request
- unless @merge_request.mergeable?
- return error('Merge request is not mergeable')
- end
+ return error('Merge request is not mergeable') unless @merge_request.mergeable?
merge_request.in_locked_state do
if commit
@@ -32,7 +29,7 @@ module MergeRequests
committer = repository.user_to_committer(current_user)
options = {
- message: commit_message,
+ message: params[:commit_message] || merge_request.merge_commit_message,
author: committer,
committer: committer
}
@@ -46,6 +43,11 @@ module MergeRequests
def after_merge
MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
+
+ if params[:should_remove_source_branch]
+ DeleteBranchService.new(@merge_request.source_project, current_user).
+ execute(merge_request.source_branch)
+ end
end
end
end
diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb
new file mode 100644
index 00000000000..d5cae2f98f7
--- /dev/null
+++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb
@@ -0,0 +1,43 @@
+module MergeRequests
+ class MergeWhenBuildSucceedsService < MergeRequests::BaseService
+ def execute(merge_request)
+ merge_request.merge_params.merge!(params)
+
+ # The service is also called when the merge params are updated.
+ already_approved = merge_request.merge_when_build_succeeds?
+
+ unless already_approved
+ merge_request.merge_when_build_succeeds = true
+ merge_request.merge_user = @current_user
+ end
+
+ merge_request.save
+
+ unless already_approved
+ SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user)
+ end
+ end
+
+ def trigger(build)
+ merge_requests = merge_request_from(build)
+
+ merge_requests.each do |merge_request|
+ next unless merge_request.merge_when_build_succeeds?
+
+ ci_commit = merge_request.ci_commit
+ if ci_commit && ci_commit.success? && merge_request.mergeable?
+ MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
+ end
+ end
+ end
+
+ private
+
+ def merge_request_from(build)
+ merge_requests = @project.origin_merge_requests.opened.where(source_branch: build.ref).to_a
+ merge_requests += @project.fork_merge_requests.opened.where(source_branch: build.ref).to_a
+
+ merge_requests.uniq.select(&:source_project)
+ end
+ end
+end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index e180edb4bf3..b26c7513f5b 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -11,6 +11,7 @@ module MergeRequests
# empty diff during a manual merge
close_merge_requests
reload_merge_requests
+ reset_merge_when_build_succeeds
# Leave a system note if a branch was deleted/added
if branch_added? || branch_removed?
@@ -57,7 +58,6 @@ module MergeRequests
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
-
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_code
merge_request.mark_as_unchecked
@@ -76,6 +76,10 @@ module MergeRequests
end
end
+ def reset_merge_when_build_succeeds
+ merge_requests_for_source_branch.each(&:reset_merge_when_build_succeeds)
+ end
+
def find_new_commits
if branch_added?
@commits = []
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 708c2f00486..5e8281a3fd0 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -130,6 +130,20 @@ class SystemNoteService
create_note(noteable: noteable, project: project, author: author, note: body)
end
+ # Called when 'merge when build succeeds' is executed
+ def self.merge_when_build_succeeds(noteable, project, author)
+ body = "This merge request will be automatically merged when the build for #{noteable.ci_commit.short_sha} succeeds"
+
+ create_note(noteable: noteable, project: project, author: author, note: body)
+ end
+
+ # Called when 'merge when build succeeds' is canceled
+ def self.cancel_merge_when_build_succeeds(noteable, project, author)
+ body = "Canceled the automatic merge"
+
+ create_note(noteable: noteable, project: project, author: author, note: body)
+ end
+
# Called when the title of a Noteable is changed
#
# noteable - Noteable object that responds to `title`
diff --git a/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml b/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml
new file mode 100644
index 00000000000..eab5be488b5
--- /dev/null
+++ b/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml
@@ -0,0 +1,2 @@
+:plain
+ $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/accept'))}");
diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml
index 33321651e32..89aae17a606 100644
--- a/app/views/projects/merge_requests/merge.js.haml
+++ b/app/views/projects/merge_requests/merge.js.haml
@@ -1,6 +1,10 @@
-- if @status
+- case @status
+- when :success
:plain
merge_request_widget.mergeInProgress();
+- when :merge_when_build_succeeds
+ :plain
+ $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_build_succeeds'))}");
- else
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}");
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
index 0aad9bb3e88..e0013fb769a 100644
--- a/app/views/projects/merge_requests/widget/_open.html.haml
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -13,6 +13,8 @@
= render 'projects/merge_requests/widget/open/conflicts'
- elsif @merge_request.work_in_progress?
= render 'projects/merge_requests/widget/open/wip'
+ - elsif @merge_request.merge_when_build_succeeds?
+ = render 'projects/merge_requests/widget/open/merge_when_build_succeeds'
- elsif !@merge_request.can_be_merged_by?(current_user)
= render 'projects/merge_requests/widget/open/not_allowed'
- elsif @merge_request.can_be_merged?
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index 9b31014b581..d2189787d47 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -1,11 +1,18 @@
-- status_class = @merge_request.ci_commit ? " ci-#{@merge_request.ci_commit.status}" : nil
+- status_class = @merge_request.ci_commit ? "ci-#{@merge_request.ci_commit.status}" : nil
= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
= hidden_field_tag :authenticity_token, form_authenticity_token
.accept-merge-holder.clearfix.js-toggle-container
.accept-action
- = f.button class: "btn btn-create accept_merge_request#{status_class}" do
- Accept Merge Request
+ - ci_commit = @merge_request.ci_commit
+ - if ci_commit && ci_commit.active?
+ = f.button class: "btn btn-create btn-grouped merge_when_build_succeeds", name: "merge_when_build_succeeds" do
+ Merge when Build Succeeds
+ = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do
+ Accept Merge Request Now
+ - else
+ = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do
+ Accept Merge Request
- if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
.accept-control.checkbox
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
@@ -21,8 +28,10 @@
rows: 14, hint: true
:javascript
- $('.accept-mr-form').on('ajax:before', function() {
- var btn = $('.accept_merge_request');
- btn.disable();
- btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
+ $('.accept_merge_request').on('click', function() {
+ $(this).html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
+ });
+
+ $('.accept-mr-form').on('ajax:send', function() {
+ $(".accept-mr-form :input").disable();
});
diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml
new file mode 100644
index 00000000000..ddd1a7bd63d
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml
@@ -0,0 +1,27 @@
+%h4
+ Approved by #{link_to_member(@project, @merge_request.merge_user, avatar: true)}
+ to be merged automatically when
+ #{link_to "the build", ci_status_path(@merge_request.ci_commit)} succeeds.
+%div
+ - if @merge_request.merge_params["should_remove_source_branch"].present?
+ = succeed '.' do
+ The changes will be merged into
+ %span.label-branch= @merge_request.target_branch
+ The source branch will be removed.
+ - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch)
+ - remove_source_branch_button = true
+ %p
+ = succeed '.' do
+ The changes will be merged into
+ %span.label-branch= @merge_request.target_branch
+ The source branch won't be removed.
+
+- if remove_source_branch_button || @merge_request.can_be_merged_by?(current_user)
+ .clearfix.prepend-top-10
+ - if remove_source_branch_button
+ = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do
+ = icon('times')
+ Remove Source Branch When Merged
+ - if @merge_request.can_be_merged_by?(current_user) || @merge_request.author == current_user
+ = link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-warning btn-sm" do
+ Cancel Automatic Merge
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index 5d1a8555b7d..c87c0a252b1 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -8,16 +8,7 @@ class MergeWorker
current_user = User.find(current_user_id)
merge_request = MergeRequest.find(merge_request_id)
- result = MergeRequests::MergeService.new(merge_request.target_project, current_user).
- execute(merge_request, params[:commit_message])
-
- if result[:status] == :success && params[:should_remove_source_branch].present?
- DeleteBranchService.new(merge_request.source_project, current_user).
- execute(merge_request.source_branch)
-
- merge_request.source_project.repository.expire_branch_names
- end
-
- result
+ MergeRequests::MergeService.new(merge_request.target_project, current_user, params).
+ execute(merge_request)
end
end
diff --git a/config/routes.rb b/config/routes.rb
index 0bc2c173453..49543786686 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -571,7 +571,7 @@ Gitlab::Application.routes.draw do
get :diffs
get :commits
post :merge
- get :merge_check
+ post :cancel_merge_when_build_succeeds
get :ci_status
post :toggle_subscription
end
diff --git a/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb
new file mode 100644
index 00000000000..ceb52f0c222
--- /dev/null
+++ b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb
@@ -0,0 +1,7 @@
+class AddMergeWhenBuildSucceedsToMergeRequest < ActiveRecord::Migration
+ def change
+ add_column :merge_requests, :merge_params, :text
+ add_column :merge_requests, :merge_when_build_succeeds, :boolean, default: false, null: false
+ add_column :merge_requests, :merge_user_id, :integer
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index aa76cef9fe4..fa32617cb99 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -421,6 +421,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do
end
add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
+ add_index "labels", ["title"], name: "index_labels_on_title", using: :btree
create_table "lfs_objects", force: true do |t|
t.string "oid", null: false
@@ -475,9 +476,9 @@ ActiveRecord::Schema.define(version: 20151116144118) do
add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree
create_table "merge_requests", force: true do |t|
- t.string "target_branch", null: false
- t.string "source_branch", null: false
- t.integer "source_project_id", null: false
+ t.string "target_branch", null: false
+ t.string "source_branch", null: false
+ t.integer "source_project_id", null: false
t.integer "author_id"
t.integer "assignee_id"
t.string "title"
@@ -486,13 +487,16 @@ ActiveRecord::Schema.define(version: 20151116144118) do
t.integer "milestone_id"
t.string "state"
t.string "merge_status"
- t.integer "target_project_id", null: false
+ t.integer "target_project_id", null: false
t.integer "iid"
t.text "description"
- t.integer "position", default: 0
+ t.integer "position", default: 0
t.datetime "locked_at"
t.integer "updated_by_id"
t.string "merge_error"
+ t.text "merge_params"
+ t.boolean "merge_when_build_succeeds", default: false, null: false
+ t.integer "merge_user_id"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
@@ -521,6 +525,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do
add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
+ add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree
create_table "namespaces", force: true do |t|
t.string "name", null: false
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index ffa7f2cdf14..1b2ad1caf49 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -2,8 +2,8 @@
## List merge requests
-Get all merge requests for this project.
-The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
+Get all merge requests for this project.
+The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
```
@@ -292,9 +292,59 @@ PUT /projects/:id/merge_request/:merge_request_id/merge
Parameters:
-- `id` (required) - The ID of a project
-- `merge_request_id` (required) - ID of MR
-- `merge_commit_message` (optional) - Custom merge commit message
+- `id` (required) - The ID of a project
+- `merge_request_id` (required) - ID of MR
+- `merge_commit_message` (optional) - Custom merge commit message
+- `should_remove_source_branch` (optional) - if `true` removes the source branch
+- `merge_when_build_succeeds` (optional) - if `true` the MR is merge when the build succeeds
+
+```json
+{
+ "id": 1,
+ "target_branch": "master",
+ "source_branch": "test1",
+ "project_id": 3,
+ "title": "test1",
+ "state": "merged",
+ "upvotes": 0,
+ "downvotes": 0,
+ "author": {
+ "id": 1,
+ "username": "admin",
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "state": "active",
+ "created_at": "2012-04-29T08:46:00Z"
+ },
+ "assignee": {
+ "id": 1,
+ "username": "admin",
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "state": "active",
+ "created_at": "2012-04-29T08:46:00Z"
+ }
+}
+```
+
+## Cancel Merge When Build Succeeds
+
+Cancels the merge when build succeeds and reset the merge parameters
+
+If successfull you'll get `200 OK`.
+
+If you don't have permissions to accept this merge request - you'll get a 401
+
+If the merge request is already merged or closed - you get 405 and error message 'Method Not Allowed'
+
+In case the merge request is not set to be merged when the build succeeds, you'll also get a 405 with the error message 'Method Not Allowed'
+```
+PUT /projects/:id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds
+```
+Parameters:
+
+- `id` (required) - The ID of a project
+- `merge_request_id` (required) - ID of MR
```json
{
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 6eb84baf9cb..f981432db36 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -179,9 +179,10 @@ module API
# Merge MR
#
# Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- # merge_commit_message (optional) - Custom merge commit message
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # merge_commit_message (optional) - Custom merge commit message
+ # merge_when_build_succeeds (optional) - truethy when this MR should be merged when the build is succesfull
# Example:
# PUT /projects/:id/merge_request/:merge_request_id/merge
#
@@ -191,34 +192,57 @@ module API
allowed = ::Gitlab::GitAccess.new(current_user, user_project).
can_push_to_branch?(merge_request.target_branch)
- if allowed
- if merge_request.unchecked?
- merge_request.check_if_can_be_merged
- end
+ # Merge request can not be merged
+ # because user dont have permissions to push into target branch
+ unauthorized! unless allowed
+
+ not_allowed! unless merge_request.open?
- if merge_request.open? && !merge_request.work_in_progress?
- if merge_request.can_be_merged?
- commit_message = params[:merge_commit_message] || merge_request.merge_commit_message
+ merge_request.check_if_can_be_merged if merge_request.unchecked?
- ::MergeRequests::MergeService.new(merge_request.target_project, current_user).
- execute(merge_request, commit_message)
+ merge_params = {
+ commit_message: params[:merge_commit_message],
+ should_remove_source_branch: params[:should_remove_source_branch]
+ }
- present merge_request, with: Entities::MergeRequest
- else
- render_api_error!('Branch cannot be merged', 405)
- end
+ if !merge_request.work_in_progress?
+ if parse_boolean(params[:merge_when_build_succeeds])
+ ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request)
else
- # Merge request can not be merged
- # because it is already closed/merged or marked as WIP
- not_allowed!
+ ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request, merge_params)
end
else
- # Merge request can not be merged
- # because user dont have permissions to push into target branch
- unauthorized!
+ render_api_error!('Branch cannot be merged', 405)
end
+
+ present merge_request, with: Entities::MergeRequest
end
+ # Cancel Merge if Merge When build succeeds is enabled
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ #
+ post ":id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+ allowed = ::Gitlab::GitAccess.new(current_user, user_project).
+ can_push_to_branch?(merge_request.target_branch)
+
+ # Merge request can not be merged
+ # because user dont have permissions to push into target branch
+ unauthorized! unless allowed
+
+ if merge_request.merged? || !merge_request.open? || !merge_request.merge_when_build_succeeds?
+ not_allowed!
+ end
+
+ merge_request.reset_merge_when_build_succeeds
+
+ present merge_request, with: Entities::MergeRequest
+ end
# Get a merge request's comments
#
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 90af75ff0e3..1bd09a1b0fb 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -31,7 +31,7 @@ describe MergeRequest do
describe 'associations' do
it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') }
it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') }
-
+ it { is_expected.to belong_to(:merge_user).class_name("User") }
it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) }
end
@@ -54,6 +54,8 @@ describe MergeRequest do
it { is_expected.to respond_to(:unchecked?) }
it { is_expected.to respond_to(:can_be_merged?) }
it { is_expected.to respond_to(:cannot_be_merged?) }
+ it { is_expected.to respond_to(:merge_params) }
+ it { is_expected.to respond_to(:merge_when_build_succeeds) }
end
describe '#to_reference' do
@@ -172,6 +174,16 @@ describe MergeRequest do
end
end
+ describe "#reset_merge_when_build_succeeds" do
+ let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true }
+ it "sets the item to false" do
+ merge_if_green.reset_merge_when_build_succeeds
+ merge_if_green.reload
+
+ expect(merge_if_green.merge_when_build_succeeds).to be_falsey
+ end
+ end
+
describe "#hook_attrs" do
it "has all the required keys" do
attrs = subject.hook_attrs
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index a45130bd473..35912ece644 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -207,6 +207,22 @@ describe SystemNoteService do
end
end
+ describe '.merge_when_build_succeeds' do
+ let(:ci_commit) { create :ci_commit, gl_project: project }
+ let(:merge_request) { create :merge_request, project: project }
+
+ subject { described_class.merge_when_build_succeeds(merge_request, project, author) }
+
+ it_behaves_like 'a system note'
+
+ it "posts the Merge When Build Succeeds system note" do
+ allow(merge_request).to receive(:ci_commit).and_return(ci_commit)
+ allow(ci_commit).to receive(:short_sha).and_return('12345678')
+
+ expect(subject.note).to eq "This merge request will be automatically merged when the build for 12345678 succeeds"
+ end
+ end
+
describe '.change_title' do
subject { described_class.change_title(noteable, project, author, 'Old title') }