# encoding: utf-8 # 如果你对改模块任何功能不清楚,请不要随便改 # @Hjqreturn class PullRequestsController < ApplicationController before_filter :authorize_logged before_filter :find_project_and_repository before_filter :connect_gitlab, :only => [:index, :show, :create, :accept_pull_request, :pull_request_commits, :pull_request_changes, :new, :update_pull_request, :pull_request_comments, :create_pull_request_comment, :compare_pull_request] before_filter :member_allowed, :only => [:new, :create, :create_pull_request_comment] before_filter :manager_allowed, :only => [:accept_pull_request] layout "base_projects" include PullRequestsHelper include ApplicationHelper require 'ostruct' # 返回json格式 def index # project_menu_type 为了控制base顶部导航 @project_menu_type = 6 # 不符合pullrequest条件的给出提示 @allow_to_pull_request = allow_pull_request(@project) > 0 type = params[:type] merge_requests = @g.merge_requests(@project.gpid) merge_requests_count = merge_requests.count case type when nil, "1" @requests = merge_requests.select{|request| request.state == "opened" || request.state == "reopened"} # 更新统计数字 project_score = @project.project_score.update_column(:pull_request_num, merge_requests_count) when "2" @requests = merge_requests.select{|request| request.state == "merged"} when "3" @requests = merge_requests.select{|request| request.state == "closed"} end @requests_opened_count = @requests.count @requests_merged_count = merge_requests.select{|request| request.state == "merged"}.count @requests_closed_count = merge_requests.select{|request| request.state == "closed"}.count @limit = 20 @is_remote = true @count = type_count(type, @requests_opened_count, @requests_merged_count, @requests_closed_count) @pages = Paginator.new @count, @limit, params['page'] || 1 @offset ||= @pages.offset @requests = paginateHelper @requests, 20 respond_to do |format| format.html format.js end end # 主要取源项目和目标项目分支及标识(用户名/版本库名) # @tip 为空的时候表明发送的pr请求有改动,为1的时候源分支和目标分支没有改动,则不能成功创建 def new # project_menu_type 为了控制base顶部导航 @project_menu_type = 6 @tip = params[:show_tip] identifier = get_rep_identifier_by_project @project @source_project_name = "#{get_user_name(@project.user_id)}/#{identifier}" @source_rev = @g.branches(@project.gpid).map{|b| b.name} # 获取forked源项目信息 if @project.forked_from_project_id @forked_project = Project.find(@project.forked_from_project_id) identifier = get_rep_identifier_by_project @forked_project @forked_project_name = "#{get_user_name(@forked_project.user_id)}/#{identifier}" @forked_rev = @g.branches(@forked_project.gpid).map{|b| b.name} end end # Creates a merge request. # If the operation is successful, 200 and the newly created merge request is returned. If an error occurs, an error number and a message explaining the reason is returned. # # @example # Gitlab.create_merge_request(5, 'New merge request', # :source_branch => 'source_branch', :target_branch => 'target_branch') # Gitlab.create_merge_request(5, 'New merge request', # :source_branch => 'source_branch', :target_branch => 'target_branch', :assignee_id => 42) # # @param [Integer] project The ID of a project. # @param [String] title The title of a merge request. # @param [Hash] options A customizable set of options. # @option options [String] :source_branch (required) The source branch name. # @option options [String] :target_branch (required) The target branch name. # @option options [Integer] :assignee_id (optional) The ID of a user to assign merge request. # @return [Gitlab::ObjectifiedHash] Information about created merge request. def create title = params[:title] description = params[:description] source_branch = params[:source_branch] target_branch = params[:target_branch] target_project_id = params[:target_project_id] begin @gitlab_address = Redmine::Configuration['gitlab_address'] git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git" job_name = Time.now.to_i # 如果分支有改动 if compare_pull_request(source_branch, target_project_id, target_branch) if @project.try(:id).to_i == 4665 # 自动生成配置Jenkinsjob,在merge事件的时候触发jenkins、sonar # merge发送的时候还没有接收,所以分析对象应该源项目 exec_jenkins(source_branch, @project.gpid, git_url, job_name) qa = QualityAnalysis.create(:project_id => @project.id, :author_login => User.current.login, :sonar_version => 1, :path => "./", :branch => source_branch, :language => "java", :sonar_name => "#{job_name}") end # 如果传送了目标项目ID即向fork源项目发送请求 # if params[:forked_project_id] && params[:source_project] == "forked_project_name" if !params[:target_project_id].blank? && params[:target_project_id].to_i != @project.id target_project_id = params[:forked_project_id].to_i request = @g.create_merge_request(@project.gpid, title, User.current.gid, :description => description, :source_branch => source_branch, :target_branch => target_branch, :target_project_id => target_project_id) @fork_project_name = Project.find(params[:target_project_id]).try(:name) @fork_pr_message = true if @fork_project_name # 向管理员发送消息 send_message_to_manager(params[:target_project_id].to_i, request.id, 1) pr = PullRequest.create(:pull_request_id => request.id, :user_id => User.current.id, :status => 1, :project_id => target_project_id, :title => title) else @project_member = Member.where(:project_id => @project.id) request = @g.create_merge_request(@project.gpid, title, User.current.gid, :description => description, :source_branch => source_branch, :target_branch => target_branch) # 发送消息 send_message_to_manager(@project.id, request.id, 1) # 创建Trustie数据 pr = PullRequest.create(:pull_request_id => request.id, :user_id => User.current.id, :status => 1, :project_id => @project.id, :title => title) respond_to do |format| format.js{redirect_to project_pull_request_path(request.id, :project_id => @project.id)} end end if @project.try(:id).to_i == 4665 qa.update_attribute(:pull_request_id, pr.id) end else tip = 1 respond_to do |format| format.js{redirect_to new_project_pull_request_path(:show_tip => tip)} end end rescue Gitlab::Error::Forbidden => e @message = l(:label_pull_request_forbidden) rescue Gitlab::Error::Conflict => e @message = l(:label_pull_request_conflic) rescue Gitlab::Error::NotFound => e @message = l(:label_pull_request_notfound) rescue Exception => e puts e end end def exec_jenkins branch, gpid, git_url, job_name # 发送PR之前先发送创建Jenkins Job、gitlab hook,调用sonar自动分析代码 @jenkins_address = Redmine::Configuration['jenkins_address'] jenkins_username = Redmine::Configuration['jenkins_username'] jenkins_password = Redmine::Configuration['jenkins_password'] # connect jenkins @client_jenkins = JenkinsApi::Client.new(:server_url => @jenkins_address, :username => jenkins_username, :password => jenkins_password) @g = Gitlab.client language = swith_language_type("java") path = "./" # qa = QualityAnalysis.where(:project_id => @project.id, :author_login => user_name).first version = 1 properties = "sonar.projectKey=#{job_name} sonar.projectName=#{job_name} sonar.projectVersion=#{version} sonar.sources=#{path} sonar.language=#{language.downcase} sonar.sourceEncoding=utf-8" # 替换配置文件 @doc = Nokogiri::XML(File.open(File.join(Rails.root, 'tmp', 'config.xml'))) @doc.at_xpath("//hudson.plugins.git.UserRemoteConfig/url").content = git_url @doc.at_xpath("//hudson.plugins.git.BranchSpec/name").content = "*/#{branch}" @doc.at_xpath("//hudson.plugins.sonar.SonarRunnerBuilder/properties").content = properties # sonar-properties # jenkins job创建 jenkins_job = @client_jenkins.job.create("#{job_name}", @doc.to_xml) # 将地址作为hook值添加到gitlab # @g.add_project_hook(@project.gpid, (@jenkins_address + "/project/#{job_name}"), merge_requests_events: true) # job创建完成后自动运行job,如果运行成功则返回‘200’ code = @client_jenkins.job.build("#{job_name}") end # Compare branch for MR # 判断源分支和目标分支是否有改动 # status 为true 表示有改动; false:便是没有改动 def compare_pull_request source_branch, target_project, target_branch user_name_source = @project.owner.try(:login) identifier = @repository.identifier.downcase git_source_tree = '--git-dir=/home/git/repositories/' + user_name_source + '/' + identifier + '.git' if target_project forked_source_project = Project.find(target_project) user_name_target = forked_source_project.owner.try(:login) git_target_tree = '--git-dir=/home/git/repositories/' + user_name_target + '/' + identifier + '.git' git_sourse_commit_id = @g.get_branch_commit_id(@project.gpid, git_source_tree, source_branch) git_target_commit_id = @g.get_branch_commit_id(forked_source_project.gpid, git_target_tree, target_branch) else git_sourse_commit_id = @g.get_branch_commit_id(@project.gpid, git_source_tree, source_branch) git_target_commit_id = @g.get_branch_commit_id(@project.gpid, git_source_tree, target_branch) end status = (git_sourse_commit_id.try(:commit_id) == git_target_commit_id.try(:commit_id) ? false : true) end # @project_menu_type 为了控制base顶部导航 # merge_when_succeeds def show # compare_pull_request source_project, source_branch, target_project, target_branch # compare_pull_request # 如何从个人主页点击进入,则将该消息设为“已读” ForgeMessage.where(:forge_message_id => params[:id], :forge_message_type => "PullRequest", :user_id => User.current.id, :viewed => false).update_all(:viewed => true) @project_menu_type = 6 @type = params[:type] @request = @g.merge_request(@project.gpid, params[:id]) @commits = @g.merge_request_commits(@project.gpid, params[:id].to_i) @commits_count = @commits.count # @commits_day = @commits.chunk @changes = @g.merge_request_changes(@project.gpid, params[:id]).try(:changes) @changes_count = @changes.count @comments = @g.merge_request_comments(@project.gpid, params[:id]).reverse @comments_count = @comments.count @limit = 10 @is_remote = true @count = @comments_count @pages = Paginator.new @count, @limit, params['page'] || 1 @offset ||= @pages.offset @comments = paginateHelper @comments, 10 end # Accept a merge request. # If merge success you get 200 OK. # Accept a merge request. # # @example # Gitlab.accept_pull_rquest(5, 1) # # @param [Integer] project The ID of a project. # @param [Integer] id The ID of a merge request. # @return [Gitlab::ObjectifiedHash] def accept_pull_request begin @status = @g.accept_merge_rquest(@project.gpid, params[:id], User.current.gid) user = User.find_by_login(@status.author.try(:username)) # 更新管理员的pullrequest消息 update_pullrequest_message(params[:id].to_i, @project.id, "PullRequest", 2) # 接受后,给用户发消息 if ForgeMessage.where(:forge_message_id => params[:id].to_i, :project_id => @project.id, :forge_message_type => "PullRequest", :user_id => user.id).count == 0 send_message_to_author(@project.id, @status.author.try(:username), params[:id], 2) end respond_to do |format| format.js{redirect_to project_pull_request_path(@status.id, :project_id => @project.id)} end rescue Gitlab::Error::Forbidden => e @message = l(:label_pull_request_forbidden) rescue Gitlab::Error::DataNotAccepted => e @message = l(:label_pull_request_datanotaccepted) rescue Gitlab::Error::NotFound => e @message = l(:label_pull_request_notfound) rescue Exception => e puts e end end # Updates a merge request. # # @example # Gitlab.update_merge_request(5, 42, :title => 'New title') # # @param [Integer] project The ID of a project. # @param [Integer] id The ID of a merge request. # @param [Hash] options A customizable set of options. # @option options [String] :title The title of a merge request. # @option options [String] :source_branch The source branch name. # @option options [String] :target_branch The target branch name. # @option options [Integer] :assignee_id The ID of a user to assign merge request. # @option options [String] :state_event New state (close|reopen|merge). # @return [Gitlab::ObjectifiedHash] Information about updated merge request. def update_pull_request begin request = @g.update_merge_request(@project.gpid, params[:id], User.current.gid, :state_event => params[:state]) user = User.find_by_login(request.author.try(:username)) status = params[:state] == "close" ? 4 : 3 #send_message_to_manager(@project.id, params[:id], status) update_pullrequest_message(request.id, @project.id, "PullRequest", status) # 给作者发送消息,如何已经发送了消息,则不发 if is_project_manager?(User.current.id, @project.id) && ForgeMessage.where(:forge_message_id => request.id, :project_id => @project.id, :forge_message_type => "PullRequest", :user_id => user.id).count == 0 send_message_to_author(@project.id, user.login, request.id, status) end respond_to do |format| format.html{redirect_to project_pull_request_path(params[:id], :project_id => @project.id)} end rescue Exception => e @message = e.message end end # Creates a merge request. # # @example # Gitlab.create_merge_request(5, 'New merge request', # :source_branch => 'source_branch', :target_branch => 'target_branch') # Gitlab.create_merge_request(5, 'New merge request', # :source_branch => 'source_branch', :target_branch => 'target_branch', :assignee_id => 42) def create_pull_request_comment content = params[:pull_request_comment] begin @comments = @g.create_merge_request_comment(@project.gpid, params[:id], content, User.current.gid) respond_to do |format| format.js{redirect_to project_pull_request_path(params[:id], :project_id => @project.id)} end rescue Exception => e @message = e.message end end # Gets the comments on a merge request. # # @example # Gitlab.merge_request_comments(5, 1) def pull_request_comments begin @comments = @g.merge_request_comments(@project.gpid, params[:id]).reverse @comments_count = @comments.count @limit = 10 @is_remote = true @count = @comments_count @pages = Paginator.new @count, @limit, params['page'] || 1 @offset ||= @pages.offset @comments = paginateHelper @comments, 10 rescue Exception => e @message = e.message end end # Get a list of merge request commits. # Parameters: # id (required) - The ID of a project # merge_request_id (required) - The ID of MR def pull_request_commits begin @type = params[:type] @commits = @g.merge_request_commits(@project.gpid, params[:id]) @commits_count = @commits.count @limit = 10 @is_remote = true @count = @commits_count @pages = Paginator.new @count, @limit, params['page'] || 1 @offset ||= @pages.offset @commits = paginateHelper @commits, 10 rescue Exception => e @message = e.message end end # Shows information about the merge request including its files and changes. With GitLab 8.2 the return fields upvotes and downvotes are deprecated and always return 0. # Parameters: # id (required) - The ID of a project # merge_request_id (required) - The ID of MR def pull_request_changes @type = params[:type] @changes = @g.merge_request_changes(@project.gpid, params[:id]).try(:changes) @changes_count = @changes.count @limit = 10 @is_remote = true @count = @changes_count @pages = Paginator.new @count, @limit, params['page'] || 1 @offset ||= @pages.offset @changes = paginateHelper @changes, 10 end private # post 相关操作权限控制 # 项目管理员可操作 def manager_allowed unless is_project_manager?(User.current.id, @project.id) return render_403 end end # 项目成员可操作 def member_allowed unless User.current.member_of?(@project) return render_403 end end def send_message user_id, project_id, pull_request_id ForgeMessage.create(:user_id => user_id, :project_id => project_id, :forge_message_id => pull_request_id, :forge_message_type => "PullRequest", :viewed => true) end def send_message_to_manager project_id, pull_request_id, status project = Project.find(project_id) project.members.each do |member| if is_project_manager?(member.user_id, project_id) && User.current.id != member.user_id add_message(member.user_id, project_id, pull_request_id, status) end end end def send_message_to_author(project_id, user_login, request_id, status) user = get_user_by_login_and(user_login) if user.id != User.current.id add_message(user.id, project_id, request_id, status) end end def add_message(user_id, project_id, pull_request_id, status) ForgeMessage.create(:user_id => user_id, :project_id => project_id, :forge_message_id => pull_request_id, :forge_message_type => "PullRequest", :viewed => false, :status => status, :operate_user_id => User.current.id, ) end def update_pullrequest_message forge_message_id, project_id, forge_message_type, status # 更新这条pullrequest消息所有相关的信息 ForgeMessage.where(:forge_message_id => forge_message_id, :project_id => project_id, :forge_message_type => forge_message_type).update_all(:status => status, :operate_user_id => User.current.id, :updated_at => Time.now) # 更新自己的消息为已读 ForgeMessage.where(:forge_message_id => forge_message_id, :project_id => project_id, :forge_message_type => forge_message_type, :user_id => User.current.id).update_all(:viewed => true) # 更新pullrequest的status字段 PullRequest.where(:pull_request_id => forge_message_id).update_all(:status => status) end def authorize_logged if !User.current.logged? redirect_to signin_path return end end def connect_gitlab @g = Gitlab.client end def find_project_and_repository @project = Project.find(params[:project_id]) render_404 if @project.gpid.blank? @repository = Repository.where(:project_id => @project.id, :type => "Repository::Gitlab").first rescue ActiveRecord::RecordNotFound render_404 end end