socialforge/app/controllers/pull_requests_controller.rb

489 lines
21 KiB
Ruby
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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