489 lines
21 KiB
Ruby
489 lines
21 KiB
Ruby
# 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'
|
||
require 'nokogiri'
|
||
require 'json'
|
||
require 'open-uri'
|
||
require 'uri'
|
||
|
||
# 返回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']
|
||
identifier = @project.repository.try(:identifier)
|
||
logger.info("1111111111111111#{identifier}")
|
||
git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git"
|
||
job_name = "pr-" + "#{Time.now.to_i}"
|
||
# 如果分支有改动
|
||
if compare_pull_request(source_branch, target_project_id, target_branch)
|
||
logger.info("2222222222222222222222")
|
||
# 自动生成配置Jenkinsjob,在merge事件的时候触发jenkins、sonar
|
||
# merge发送的时候还没有接收,所以分析对象应该源项目
|
||
language = swith_language_type(@project.language)
|
||
# exec_jenkins(source_branch, @project.gpid, git_url, job_name, language)
|
||
logger.info("33333333333333333333")
|
||
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}")
|
||
logger.info("4444444444444444444444444")
|
||
|
||
# 如果传送了目标项目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
|
||
# 关联pr和qa
|
||
qa.update_attribute(:pull_request_id, request.try(:id))
|
||
|
||
else
|
||
tip = 1
|
||
respond_to do |format|
|
||
format.js{redirect_to new_project_pull_request_path(:show_tip => tip)}
|
||
end
|
||
end
|
||
# rescue Exception => e
|
||
# logger.info("create PR failed ####{e}")
|
||
# end
|
||
end
|
||
|
||
def exec_jenkins branch, gpid, git_url, job_name, language
|
||
# 发送PR之前先发送创建Jenkins Job、gitlab hook,调用sonar自动分析代码
|
||
logger.info("5555555555555555#{branch}, git_url#{git_url}, job_name #{job_name}")
|
||
|
||
@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 = language.present? ? language : "java"
|
||
path = "./"
|
||
# qa = QualityAnalysis.where(:project_id => @project.id, :author_login => user_name).first
|
||
version = 1
|
||
if language == "java"
|
||
properties = "sonar.projectKey=#{job_name}
|
||
sonar.projectName=#{job_name}
|
||
sonar.projectVersion=#{version}
|
||
sonar.sources=#{path}
|
||
sonar.language=#{language.downcase}
|
||
sonar.sourceEncoding=utf-8
|
||
sonar.java.binaries=./"
|
||
else
|
||
properties = "sonar.projectKey=#{job_name}
|
||
sonar.projectName=#{job_name}
|
||
sonar.projectVersion=#{version}
|
||
sonar.sources=#{path}
|
||
sonar.language=#{language.downcase}
|
||
sonar.sourceEncoding=utf-8"
|
||
end
|
||
|
||
# 替换配置文件
|
||
@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
|
||
logger.info("6666666666666666666666666")
|
||
# jenkins job创建
|
||
jenkins_job = @client_jenkins.job.create("#{job_name}", @doc.to_xml)
|
||
logger.info("777777777777777777")
|
||
# 将地址作为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_sourse_commit_id = @g.commits(@project.gpid, :ref_name => source_branch).first.try(:id)
|
||
# git_target_commit_id = @g.get_branch_commit_id(forked_source_project.gpid, git_target_tree, target_branch)
|
||
git_target_commit_id = @g.commits(forked_source_project.gpid, :ref_name => target_branch).first.try(:id)
|
||
else
|
||
git_sourse_commit_id = @g.commits(@project.gpid, :ref_name => source_branch).first.try(:id)
|
||
git_target_commit_id = @g.commits(@project.gpid, :ref_name => target_branch).first.try(:id)
|
||
end
|
||
logger.info("compare source_branch################{source_branch}")
|
||
logger.info("compare target_project################{target_project}")
|
||
logger.info("compare git_sourse_commit_id################{git_sourse_commit_id}")
|
||
logger.info("compare git_target_commit_id################{git_target_commit_id}")
|
||
status = (git_sourse_commit_id == git_target_commit_id ? false : true)
|
||
end
|
||
|
||
# 获取jenkins输出结果
|
||
def pr_console
|
||
job_name = params[:job_name]
|
||
pr = PullRequest.where(:pull_request_id => params[:pr_id]).first
|
||
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)
|
||
@output = client_jenkins.job.get_console_output("#{job_name}", build_num = 0, start = 0, mode = 'html')["output"]
|
||
@output = @output.force_encoding("UTF-8") if @output.present?
|
||
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
|
||
logger.info 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
|