diff options
-rw-r--r-- | app/assets/javascripts/merge_requests.js | 18 | ||||
-rw-r--r-- | app/controllers/merge_requests_controller.rb | 20 | ||||
-rw-r--r-- | app/models/ability.rb | 1 | ||||
-rw-r--r-- | app/models/merge_request.rb | 48 | ||||
-rw-r--r-- | app/models/project/hooks_trait.rb | 2 | ||||
-rw-r--r-- | app/views/merge_requests/automerge.js.haml | 7 | ||||
-rw-r--r-- | app/views/merge_requests/show.html.haml | 37 | ||||
-rw-r--r-- | config/routes.rb | 2 | ||||
-rw-r--r-- | db/migrate/20120329170745_add_automerge_to_merge_request.rb | 5 | ||||
-rw-r--r-- | db/schema.rb | 4 | ||||
-rw-r--r-- | lib/gitlab_merge.rb | 48 | ||||
-rw-r--r-- | lib/gitlabhq/gitolite.rb | 29 | ||||
-rw-r--r-- | lib/tasks/gitlab_enable_automerge.rake | 9 |
13 files changed, 223 insertions, 7 deletions
diff --git a/app/assets/javascripts/merge_requests.js b/app/assets/javascripts/merge_requests.js index e87071b5404..239ea85c070 100644 --- a/app/assets/javascripts/merge_requests.js +++ b/app/assets/javascripts/merge_requests.js @@ -1,9 +1,19 @@ var MergeRequest = { diffs_loaded: false, commits_loaded: false, + opts: false, init: - function() { + function(opts) { + this.opts = opts; + + if($(".automerge_widget").length){ + $.get(opts.url_to_automerge_check, function(data){ + $(".automerge_widget").hide(); + $(".automerge_widget." + data.state).show(); + }, "json"); + } + $(".nav-tabs a").live("click", function() { $(".nav-tabs a").parent().removeClass("active"); $(this).parent().addClass("active"); @@ -44,5 +54,11 @@ var MergeRequest = { function() { $(".first_mr_commits").remove(); $(".all_mr_commits").removeClass("hide"); + }, + + already_cannot_be_merged: + function(){ + $(".automerge_widget").hide(); + $(".automerge_widget.already_cannot_be_merged").show(); } } diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index 500406c919c..7a803e2db65 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -2,7 +2,7 @@ class MergeRequestsController < ApplicationController before_filter :authenticate_user! before_filter :project before_filter :module_enabled - before_filter :merge_request, :only => [:edit, :update, :destroy, :show, :commits, :diffs] + before_filter :merge_request, :only => [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check] layout "project" # Authorize @@ -89,6 +89,7 @@ class MergeRequestsController < ApplicationController respond_to do |format| if @merge_request.update_attributes(params[:merge_request].merge(:author_id_of_changes => current_user.id)) @merge_request.reload_code + @merge_request.mark_as_unchecked format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' } format.json { head :ok } else @@ -98,6 +99,23 @@ class MergeRequestsController < ApplicationController end end + def automerge_check + if @merge_request.unchecked? + @merge_request.check_if_can_be_merged + end + render :json => {:state => @merge_request.human_state} + end + + def automerge + return access_denied! unless can?(current_user, :accept_mr, @project) + if @merge_request.open? && @merge_request.can_be_merged? + @merge_request.automerge!(current_user) + @status = true + else + @status = false + end + end + def destroy @merge_request.destroy diff --git a/app/models/ability.rb b/app/models/ability.rb index 5792948fa51..d65695a2079 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -48,6 +48,7 @@ class Ability :admin_team_member, :admin_merge_request, :admin_note, + :accept_mr, :admin_wiki ] if project.master_access_for?(user) || project.owner == user diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index edf073d4e88..248bbdacad4 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1,6 +1,10 @@ require File.join(Rails.root, "app/models/commit") class MergeRequest < ActiveRecord::Base + UNCHECKED = 1 + CAN_BE_MERGED = 2 + CANNOT_BE_MERGED = 3 + belongs_to :project belongs_to :author, :class_name => "User" belongs_to :assignee, :class_name => "User" @@ -45,6 +49,15 @@ class MergeRequest < ActiveRecord::Base where("source_branch like :branch or target_branch like :branch", :branch => branch_name) end + def human_state + states = { + CAN_BE_MERGED => "can_be_merged", + CANNOT_BE_MERGED => "cannot_be_merged", + UNCHECKED => "unchecked" + } + states[self.state] + end + def validate_branches if target_branch == source_branch errors.add :base, "You can not use same branch for source and target branches" @@ -56,6 +69,27 @@ class MergeRequest < ActiveRecord::Base self.reloaded_diffs end + def unchecked? + state == UNCHECKED + end + + def mark_as_unchecked + self.update_attributes(:state => UNCHECKED) + end + + def can_be_merged? + state == CAN_BE_MERGED + end + + def check_if_can_be_merged + self.state = if GitlabMerge.new(self, self.author).can_be_merged? + CAN_BE_MERGED + else + CANNOT_BE_MERGED + end + self.save + end + def new? today? && created_at == updated_at end @@ -118,6 +152,10 @@ class MergeRequest < ActiveRecord::Base save end + def mark_as_unmergable + self.update_attributes :state => CANNOT_BE_MERGED + end + def reloaded_commits if open? && unmerged_commits.any? self.st_commits = unmerged_commits @@ -144,6 +182,16 @@ class MergeRequest < ActiveRecord::Base :author_id => user_id ) end + + def automerge!(current_user) + if GitlabMerge.new(self, current_user).merge + self.merge!(current_user.id) + true + end + rescue + self.mark_as_unmergable + false + end end # == Schema Information # diff --git a/app/models/project/hooks_trait.rb b/app/models/project/hooks_trait.rb index c3a4f6139a3..f642ac978f3 100644 --- a/app/models/project/hooks_trait.rb +++ b/app/models/project/hooks_trait.rb @@ -18,7 +18,7 @@ module Project::HooksTrait # Update code for merge requests mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all - mrs.each { |merge_request| merge_request.reload_code } + mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } # Close merge requests mrs = self.merge_requests.opened.where(:target_branch => branch_name).all diff --git a/app/views/merge_requests/automerge.js.haml b/app/views/merge_requests/automerge.js.haml new file mode 100644 index 00000000000..93e184455af --- /dev/null +++ b/app/views/merge_requests/automerge.js.haml @@ -0,0 +1,7 @@ +-if @status + :plain + location.reload(); +-else + :plain + MergeRequest.already_cannot_be_merged() + diff --git a/app/views/merge_requests/show.html.haml b/app/views/merge_requests/show.html.haml index 8402a66ed19..56345c394c8 100644 --- a/app/views/merge_requests/show.html.haml +++ b/app/views/merge_requests/show.html.haml @@ -8,7 +8,6 @@ %span.right - if can?(current_user, :modify_merge_request, @merge_request) - if @merge_request.open? - = link_to "Show how to merge", "#", :class => "how_to_merge_link btn small padded", :title => "How To Merge" = link_to 'Close', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => true }, :status_only => true), :method => :put, :class => "btn small padded", :title => "Close merge request" = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn small padded" do Edit @@ -53,6 +52,34 @@ Closed by #{@merge_request.closed_event.author_name} %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago. + +- if @merge_request.open? && @commits.any? && can?(current_user, :accept_mr, @project) + .automerge_widget.can_be_merged{:style => "display:none"} + .ui-box.padded + %p + You can accept this request automatically. If you still want to do it manually - #{link_to "click here", "#", :class => "how_to_merge_link vlink", :title => "How To Merge"} for instructions + = link_to "Accept Merge Request", automerge_project_merge_request_path(@project, @merge_request), :class => "btn small info accept_merge_request", :remote => true + + + .automerge_widget.cannot_be_merged{:style => "display:none"} + .alert-message + %p + %strong This request cant be merged with GitLab. You should do it manually + = link_to "Show how to merge", "#", :class => "how_to_merge_link btn small padded", :title => "How To Merge" + + .automerge_widget.unchecked + .alert-message + %p + %strong Checking for ability to automatically mergeā¦ + + .automerge_widget.already_cannot_be_merged{:style => "display:none"} + .alert-message + %p + %strong This merge request already can not be merged + + + + = render "merge_requests/commits" - unless @commits.empty? @@ -72,7 +99,13 @@ :javascript $(function(){ - MergeRequest.init(); + MergeRequest.init({ + url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}", + }); + + $(".accept_merge_request").live("ajax:beforeSend", function() { + $(this).replaceWith('#{image_tag "ajax_loader.gif"}'); + }) }) = render "notes/per_line_form" diff --git a/config/routes.rb b/config/routes.rb index a6e32bc1f5b..681bffc9506 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -100,6 +100,8 @@ Gitlab::Application.routes.draw do resources :merge_requests do member do get :diffs + get :automerge + get :automerge_check end collection do diff --git a/db/migrate/20120329170745_add_automerge_to_merge_request.rb b/db/migrate/20120329170745_add_automerge_to_merge_request.rb new file mode 100644 index 00000000000..de7c68ee1cb --- /dev/null +++ b/db/migrate/20120329170745_add_automerge_to_merge_request.rb @@ -0,0 +1,5 @@ +class AddAutomergeToMergeRequest < ActiveRecord::Migration + def change + add_column :merge_requests, :state, :integer, :null => false, :default => 1 + end +end diff --git a/db/schema.rb b/db/schema.rb index b1419a4f6d8..fc37d0b0139 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,8 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120413135904) do - +ActiveRecord::Schema.define(:version => 20120329170745) do create_table "events", :force => true do |t| t.string "target_type" t.integer "target_id" @@ -65,6 +64,7 @@ ActiveRecord::Schema.define(:version => 20120413135904) do t.text "st_commits", :limit => 2147483647 t.text "st_diffs", :limit => 2147483647 t.boolean "merged", :default => false, :null => false + t.boolean "auto_merge", :default => true, :null => false end add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" diff --git a/lib/gitlab_merge.rb b/lib/gitlab_merge.rb new file mode 100644 index 00000000000..66a0e7ec635 --- /dev/null +++ b/lib/gitlab_merge.rb @@ -0,0 +1,48 @@ +class GitlabMerge + attr_accessor :project, :merge_path, :merge_request, :user + + def initialize(merge_request, user) + self.user = user + self.merge_request = merge_request + self.project = merge_request.project + self.merge_path = File.join(Rails.root, "tmp", "merge_repo", project.path, merge_request.id.to_s) + FileUtils.rm_rf(merge_path) + FileUtils.mkdir_p merge_path + end + + def can_be_merged? + pull do |repo, output| + !(output =~ /Automatic merge failed/) + end + end + + def merge + pull do |repo, output| + if output =~ /Automatic merge failed/ + false + else + repo.git.push({}, "origin", merge_request.target_branch) + true + end + end + end + + def pull + File.open(File.join(Rails.root, "tmp", "merge_repo", "#{project.path}.lock"), "w+") do |f| + f.flock(File::LOCK_EX) + + self.project.repo.git.clone({:branch => merge_request.target_branch}, project.url_to_repo, merge_path) + unless File.exist?(self.merge_path) + raise "Gitlab user do not have access to repo. You should run: rake gitlab_enable_automerge" + end + Dir.chdir(merge_path) do + merge_repo = Grit::Repo.new('.') + merge_repo.git.sh "git config user.name \"#{user.name}\"" + merge_repo.git.sh "git config user.email \"#{user.email}\"" + output = merge_repo.git.pull({}, "--no-ff", "origin", merge_request.source_branch) + yield(merge_repo, output) + end + + end + end +end diff --git a/lib/gitlabhq/gitolite.rb b/lib/gitlabhq/gitolite.rb index 701b9a8f8b4..fabeb7d0fca 100644 --- a/lib/gitlabhq/gitolite.rb +++ b/lib/gitlabhq/gitolite.rb @@ -123,5 +123,34 @@ module Gitlabhq repo end + + def admin_all_repo + ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite')) + conf = ga_repo.config + owner_name = "" + + # Read gitolite-admin user + # + begin + repo = conf.get_repo("gitolite-admin") + owner_name = repo.permissions[0]["RW+"][""][0] + raise StandardError if owner_name.blank? + rescue => ex + puts "Cant determine gitolite-admin owner".red + raise StandardError + end + + # @ALL repos premission for gitolite owner + repo_name = "@all" + repo = if conf.has_repo?(repo_name) + conf.get_repo(repo_name) + else + ::Gitolite::Config::Repo.new(repo_name) + end + + repo.add_permission("RW+", "", owner_name) + conf.add_repo(repo, true) + ga_repo.save + end end end diff --git a/lib/tasks/gitlab_enable_automerge.rake b/lib/tasks/gitlab_enable_automerge.rake new file mode 100644 index 00000000000..6ff5003eac1 --- /dev/null +++ b/lib/tasks/gitlab_enable_automerge.rake @@ -0,0 +1,9 @@ +desc "Give gitlab user full access to every repo" +task :gitlab_enable_automerge => :environment do + + Gitlabhq::GitHost.system.new.configure do |git| + git.admin_all_repo + end + + puts "Done!".green +end |