Skip to content
Snippets Groups Projects
Commit 73c59de0 authored by Douwe Maan's avatar Douwe Maan
Browse files

Merge branch 'master' into split-up-builds

parents 6c6fb1d8 9bfd6c44
No related branches found
No related tags found
No related merge requests found
Showing
with 300 additions and 102 deletions
Please view this file on the master branch, on stable branches it's out of date.
 
v 8.3.0 (unreleased)
- Merge when build succeeds (Zeger-Jan van de Weg)
- Bump gollum-lib to 4.1.0 (Stan Hu)
- Fix broken group avatar upload under "New group" (Stan Hu)
- Update project repositorize size and commit count during import:repos task (Stan Hu)
Loading
Loading
Loading
Loading
@@ -18,6 +18,7 @@
.accept-merge-holder {
.accept-action {
display: inline-block;
float: left;
 
.accept_merge_request {
&.ci-pending,
Loading
Loading
@@ -36,14 +37,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;
}
Loading
Loading
Loading
Loading
@@ -2,11 +2,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :builds, :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, :builds]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
 
# Allow read any merge_request
Loading
Loading
@@ -164,15 +165,29 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
 
def cancel_merge_when_build_succeeds
return access_denied! unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user).cancel(@merge_request)
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 && @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
 
Loading
Loading
@@ -287,6 +302,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
 
def define_widget_vars
@ci_commit = @merge_request.ci_commit
end
def invalid_mr
# Render special view for MR with removed source or target branch
render 'invalid'
Loading
Loading
@@ -300,6 +319,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
)
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
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
# == 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
Loading
Loading
@@ -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'
Loading
Loading
Loading
Loading
@@ -2,25 +2,28 @@
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# merge_params :text (serialized to hash)
# merge_when_build_succeeds :boolean default(false), not null
# merge_user_id :integer
#
 
require Rails.root.join("app/models/commit")
Loading
Loading
@@ -35,9 +38,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
 
Loading
Loading
@@ -121,6 +127,7 @@ class MergeRequest < ActiveRecord::Base
validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true
validates :merge_user, presence: true, if: :merge_when_build_succeeds?
validate :validate_branches
validate :validate_fork
 
Loading
Loading
@@ -258,6 +265,16 @@ class MergeRequest < ActiveRecord::Base
end
end
 
def can_cancel_merge_when_build_succeeds?(current_user)
can_be_merged_by?(current_user) || self.author == current_user
end
def can_remove_source_branch?(current_user)
!source_project.protected_branch?(source_branch) &&
!source_project.root_ref?(source_branch) &&
Ability.abilities.allowed?(current_user, :push_code, source_project)
end
def mr_and_commit_notes
# Fetch comments only from last 100 commits
commits_for_notes_limit = 100
Loading
Loading
@@ -393,6 +410,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
Loading
Loading
@@ -480,8 +507,6 @@ class MergeRequest < ActiveRecord::Base
end
 
def ci_commit
if last_commit and source_project
source_project.ci_commit(last_commit.id)
end
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
end
end
Loading
Loading
@@ -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
Loading
Loading
@@ -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
}
Loading
Loading
@@ -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
module MergeRequests
class MergeWhenBuildSucceedsService < MergeRequests::BaseService
# Marks the passed `merge_request` to be merged when the build succeeds or
# updates the params for the automatic merge
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
SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.last_commit)
end
merge_request.save
end
# Triggers the automatic merge of merge_request once the build succeeds
def trigger(build)
merge_requests = merge_request_from(build)
merge_requests.each do |merge_request|
next unless merge_request.merge_when_build_succeeds?
if merge_request.ci_commit && merge_request.ci_commit.success? && merge_request.mergeable?
MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
end
end
end
# Cancels the automatic merge
def cancel(merge_request)
if merge_request.merge_when_build_succeeds? && merge_request.open?
merge_request.reset_merge_when_build_succeeds
SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user)
success
else
error("Can't cancel the automatic merge", 406)
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
Loading
Loading
@@ -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?
Loading
Loading
@@ -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
Loading
Loading
@@ -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 = []
Loading
Loading
Loading
Loading
@@ -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, last_commit)
body = "Enabled an automatic merge when the build for #{last_commit.to_reference(project)} 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`
Loading
Loading
- ci_commit = merge_request.ci_commit
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
%span.merge-request-title-text
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
.pull-right.light
- if ci_commit
= render_ci_status(ci_commit)
- if merge_request.ci_commit
= render_ci_status(merge_request.ci_commit)
- if merge_request.merged?
%span
= icon('check')
Loading
Loading
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/accept'))}");
- if @status
- case @status
- when :success
:plain
merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
- 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'))}");
Loading
Loading
@@ -14,7 +14,7 @@
= @merge_request.target_branch
The source branch has been removed.
 
- elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch)
- elsif @merge_request.can_remove_source_branch?(current_user)
.remove_source_branch_widget
%p
= succeed '.' do
Loading
Loading
@@ -50,5 +50,3 @@
$('.remove_source_branch_in_progress').hide();
$('.remove_source_branch_widget.failed').show();
});
Loading
Loading
@@ -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?
Loading
Loading
Loading
Loading
@@ -3,26 +3,60 @@
= 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
- 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
= check_box_tag :should_remove_source_branch
Remove source branch
.accept-control.right
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
= icon('edit')
Modify commit message
.js-toggle-content.hide.prepend-top-20
.clearfix
.accept-action
- if @ci_commit && @ci_commit.active?
%span.btn-group
= link_to "#", class: "btn btn-create merge_when_build_succeeds" do
Merge When Build Succeeds
%a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%span.caret
%span.sr-only
Select Merge Moment
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li
= link_to "#", class: "merge_when_build_succeeds" do
= icon('check fw')
Merge When Build Succeeds
%li
= link_to "#", class: "accept_merge_request" do
= icon('warning fw')
Merge Immediately
- else
= f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do
Accept Merge Request
- if @merge_request.can_remove_source_branch?(current_user)
.accept-control.checkbox
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
= check_box_tag :should_remove_source_branch
Remove source branch
.accept-control.right
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
= icon('edit')
Modify commit message
.js-toggle-content.hide.prepend-top-default
= render 'shared/commit_message_container', params: params,
text: @merge_request.merge_commit_message,
rows: 14, hint: true
 
= hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
: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();
});
$('a.accept_merge_request').on('click', function(e) {
e.preventDefault();
$(this).closest("form").submit();
});
$('a.merge_when_build_succeeds').on('click', function(e) {
e.preventDefault();
$("#merge_when_build_succeeds").val("1");
$(this).closest("form").submit();
});
%h4
Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)}
to be merged automatically when the build succeeds.
%div
- should_remove_source_branch = @merge_request.merge_params["should_remove_source_branch"].present?
%p
= succeed '.' do
The changes will be merged into
%span.label-branch= @merge_request.target_branch
- if should_remove_source_branch
The source branch will be removed.
- else
The source branch will not be removed.
- remove_source_branch_button = @merge_request.can_remove_source_branch?(current_user) && !should_remove_source_branch
- user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
- if remove_source_branch_button || user_can_cancel_automatic_merge
.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 user_can_cancel_automatic_merge
= 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
Loading
Loading
@@ -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
Loading
Loading
@@ -573,8 +573,9 @@ Rails.application.routes.draw do
get :commits
get :diffs
get :builds
post :merge
get :merge_check
post :merge
post :cancel_merge_when_build_succeeds
get :ci_status
post :toggle_subscription
end
Loading
Loading
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment