socialforge/app/controllers/quality_analysis_controller.rb

318 lines
13 KiB
Ruby
Raw Normal View History

2016-06-21 16:50:45 +08:00
class QualityAnalysisController < ApplicationController
2016-06-17 11:29:49 +08:00
before_filter :find_project_by_project_id#, :except => [:getattachtype]
2016-07-01 09:31:05 +08:00
before_filter :find_quality_analysis, :only => [:edit, :update_jenkins_job]
2016-06-21 16:46:34 +08:00
before_filter :authorize
2016-07-12 16:23:30 +08:00
before_filter :connect_jenkins, :only => [:create, :edit, :update_jenkins_job, :index, :delete]
2016-06-17 11:29:49 +08:00
layout "base_projects"
include ApplicationHelper
2016-07-01 14:39:40 +08:00
include QualityAnalysisHelper
2016-11-17 11:32:39 +08:00
require 'jenkins_api_client'
require 'nokogiri'
require 'json'
require 'open-uri'
2016-06-22 17:09:54 +08:00
def show
end
2016-07-01 14:39:40 +08:00
# params 说明:{identifier版本库名}
# type: 1 新的分析 2 重新分析
def create
2016-06-28 09:45:45 +08:00
begin
user_name = User.find(params[:user_id]).try(:login)
identifier = params[:identifier]
2016-06-28 14:48:09 +08:00
rep_id = params[:rep_id]
2016-07-01 14:39:40 +08:00
# job_name and sonar_name 前者为job名字后者为jenkins配置名
2016-06-28 14:48:09 +08:00
job_name = "#{user_name}-#{rep_id}"
2016-06-28 17:18:50 +08:00
sonar_name = "#{user_name}:#{rep_id}"
2016-06-28 09:45:45 +08:00
2016-07-08 11:27:03 +08:00
# 考虑到历史数据有些用户创建类job但是build失败,即sonar没有结果这个时候需要把job删除,并且删掉quality_analyses表数据
# 如果不要这句则需要迁移数据
@sonar_address = Redmine::Configuration['sonar_address']
# projects_date = open(@sonar_address + "/api/projects/index").read
# arr = JSON.parse(projects_date).map {|m| m["nm"]} # eg: ["Hjqreturn:cc_rep", "Hjqreturn:putong", "Hjqreturn:sonar_rep2", "shitou:sonar_rep"]
quality_an = QualityAnalysis.where(:sonar_name => sonar_name)
# if @client_jenkins.job.exists?(job_name) && QualityAnalysis.where(:sonar_name => sonar_name).select{|qa| arr.include?(qa.sonar_name)}.blank?
# aa = @client_jenkins.job.delete("#{job_name}")
# quality_an.delete unless quality_an.blank?
# end
# type 1的时候之所以判断job是否存在为了防止特殊情况正常情况是不会出现的
# 重新分析的时候需要删除以前的分析结果
@client_jenkins.job.delete("#{job_name}") if @client_jenkins.job.exists?(job_name)
quality_an.delete_all unless quality_an.blank?
2016-08-10 10:58:47 +08:00
2016-06-28 09:45:45 +08:00
# Checks if the given job exists in Jenkins.
@g = Gitlab.client
branch = params[:branch]
language = swith_language_type(params[:language])
path = params[:path].blank? ? "./" : params[:path]
# qa = QualityAnalysis.where(:project_id => @project.id, :author_login => user_name).first
version = 1
properties = "sonar.projectKey=#{sonar_name}
2016-06-28 17:18:50 +08:00
sonar.projectName=#{sonar_name}
2016-06-22 17:09:54 +08:00
sonar.projectVersion=#{version}
sonar.sources=#{path}
2016-06-21 16:46:34 +08:00
sonar.language=#{language.downcase}
sonar.sourceEncoding=utf-8"
git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git"
# 替换配置文件
@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}")
# job创建完成后自动运行job,如果运行成功则返回200
code = @client_jenkins.job.build("#{job_name}")
# 判断调用sonar分析是否成功
# 等待启动时间处理, 最长时间为30分钟
for i in 0..360 do
sleep(5)
@current_build_status = @client_jenkins.job.get_current_build_status("#{job_name}")
if (@current_build_status == "success" || @current_build_status == "failure")
break
if i == 360
@build_console_result = false
2016-07-01 14:39:40 +08:00
break
end
end
end
2016-07-01 14:39:40 +08:00
# sonar 缓冲sonar生成数据
sleep(10)
2016-07-08 20:35:21 +08:00
# 获取sonar output结果
console_build = @client_jenkins.job.get_console_output("#{job_name}", build_num = 0, start = 0, mode = 'text')["output"]
logger.info("@current_build_status is ==> #{@current_build_status}")
2016-07-07 14:22:48 +08:00
# 两种情况需要删除job
# 1/创建成功但是build失败则删除job
# 2/creat和build成功调用sonar启动失败则删除job
# 错误信息存储需存到Trustie数据库否则一旦job删除则无法获取这些信息
if jenkins_job == '200' && code != '201'
@client_jenkins.job.delete("#{job_name}")
else
if @current_build_status == "failure"
reg_console = /Exception:.*?\r/.match(console_build)
output = reg_console[0].gsub("\r", "") unless reg_console.nil?
se = SonarError.where(:jenkins_job_name => job_name).first
se.nil? ? SonarError.create(:project_id => @project.id, :jenkins_job_name => job_name, :output => output) : se.update_column(:output, output)
2016-08-10 10:58:47 +08:00
@client_jenkins.job.delete("#{job_name}")
elsif @current_build_status == "success"
if quality_an.blank?
QualityAnalysis.create(:project_id => @project.id, :author_login => user_name, :rep_identifier => identifier,
:sonar_version => version, :path => path, :branch => branch, :language => language, :sonar_name => "#{user_name}:#{rep_id}")
2016-07-07 14:22:48 +08:00
end
2016-06-28 09:45:45 +08:00
end
end
2016-08-10 10:58:47 +08:00
respond_to do |format|
if @current_build_status == "success"
format.html{redirect_to project_quality_analysis_path(:project_id => @project.id, :resource_id => sonar_name, :branch => branch, :current_build_status => @current_build_status, :job_name => job_name)}
elsif @current_build_status == "failure"
format.html{redirect_to error_list_project_quality_analysi_path(:project_id => @project.id, :job_name => job_name)}
2016-08-10 10:58:47 +08:00
end
end
2016-06-28 09:45:45 +08:00
rescue => e
2016-07-11 16:59:37 +08:00
@message = e.message
2016-08-10 10:58:47 +08:00
logger.error("######################====>#{e.message}")
respond_to do |format|
format.html{redirect_to error_list_project_quality_analysi_path(:project_id => @project.id, :job_name => job_name, :message => @message)}
# format.html{redirect_to :controller => 'repositories', :action => 'show', :id => @project, :repository_id => identifier}
2016-07-07 11:08:29 +08:00
end
2016-07-01 14:39:40 +08:00
end
2016-08-10 10:58:47 +08:00
end
2016-06-17 11:29:49 +08:00
2016-07-07 14:22:48 +08:00
def error_list
2016-10-28 09:05:37 +08:00
# 顶部导航
@project_menu_type = 5
2016-07-07 14:22:48 +08:00
@error_list = SonarError.where(:jenkins_job_name => params[:job_name]).first
respond_to do |format|
format.html
end
end
2016-06-29 09:59:21 +08:00
# get language type
def swith_language_type language
2016-06-29 11:10:19 +08:00
if language == "c#"
2016-06-29 09:59:21 +08:00
"cs"
2016-06-29 11:10:19 +08:00
elsif language == "python"
2016-06-29 09:59:21 +08:00
"py"
2016-06-29 11:10:19 +08:00
elsif language == "c"
2016-06-29 09:59:21 +08:00
"c++"
else
language
end
end
2016-06-30 10:17:53 +08:00
def edit
2016-07-01 09:31:05 +08:00
@g = Gitlab.client
gitlab_branches = @g.branches(@project.gpid)
@branch_names = gitlab_branches.map{|b| b.name}
@gitlab_default_branch = @g.project(@project.gpid).default_branch
2016-06-30 10:17:53 +08:00
end
2016-07-12 16:23:30 +08:00
# 删除的时候主要删除三方面数据1/Trustie数据 2/jenkins数据 3/sonar数据
# 如果只删除数据1则新建的时候会有冲突
def delete
begin
qa = QualityAnalysis.find(params[:id])
rep_id = Repository.where(:project_id => @project.id, :identifier => qa.rep_identifier).first.try(:id)
job_name = "#{qa.author_login}-#{rep_id}"
logger.info("result: job_name ###################==>#{job_name}")
2016-08-10 10:58:47 +08:00
logger.info("result: @client_jenkins.job ###################==>#{@client_jenkins.job}")
2016-07-12 16:23:30 +08:00
2016-08-10 10:58:47 +08:00
d_job = @client_jenkins.job.delete(job_name)
2016-07-12 16:23:30 +08:00
logger.info("result: delete job ###################==>#{d_job}")
qa.delete
respond_to do |format|
format.html{redirect_to :controller => 'repositories', :action => 'show', :id => @project, :repository_id => gitlab_repository(@project).identifier}
2016-07-12 16:23:30 +08:00
end
rescue Exception => e
puts e
end
end
2016-07-01 14:39:40 +08:00
# 更新Jenkins job主要包括相关配置文件参数的更新Trustie平台数据的更新
2016-07-01 09:31:05 +08:00
def update_jenkins_job
begin
rep_id = Repository.where(:project_id => @project.id).first.try(:id)
sonar_name = @quality_analysis.sonar_name
job_name = "#{@quality_analysis.author_login}-#{rep_id}"
version = @quality_analysis.sonar_version
path = params[:path].blank? ? "./" : params[:path]
language = swith_language_type(params[:language])
branch = params[:branch]
identifier = @quality_analysis.rep_identifier
properties = "sonar.projectKey=#{sonar_name}
sonar.projectName=#{sonar_name}
sonar.projectVersion=#{version}
sonar.sources=#{path}
sonar.language=#{language.downcase}
sonar.sourceEncoding=utf-8"
git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git"
# modify config.yml
@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
2016-07-01 14:39:40 +08:00
# update成功则返回 200
2016-08-10 10:58:47 +08:00
jenkins_job = @client_jenkins.job.update("#{job_name}", @doc.to_xml)
2016-07-07 11:08:29 +08:00
# 数据更新到Trustie数据
2016-07-01 09:31:05 +08:00
if jenkins_job == '200'
logger.info("quality_ananlysis will be updated: ==> #{jenkins_job}")
@quality_analysis.path = path
@quality_analysis.language = language
@quality_analysis.branch = branch
@quality_analysis.save
end
rescue Exception => e
logger.error("Update jenkins job: #{e}")
end
respond_to do |format|
format.html{redirect_to project_quality_analysis_path(:project_id => @project.id)}
format.js
end
2016-06-30 10:17:53 +08:00
end
2016-06-28 14:48:09 +08:00
# resource_id: login + @repository.id
2016-06-17 11:29:49 +08:00
def index
2016-10-28 09:05:37 +08:00
# 顶部导航
@project_menu_type = 5
2016-06-28 14:48:09 +08:00
begin
2016-07-01 21:31:50 +08:00
@branch = params[:branch]
2016-06-28 14:48:09 +08:00
@resource_id = params[:resource_id]
@sonar_address = Redmine::Configuration['sonar_address']
if params[:resource_id].nil?
@name_flag = true
projects_date = open(@sonar_address + "/api/projects/index").read
arr = JSON.parse(projects_date).map {|m| m["nm"]} # eg: ["Hjqreturn:cc_rep", "Hjqreturn:putong", "Hjqreturn:sonar_rep2", "shitou:sonar_rep"]
2016-06-29 09:38:30 +08:00
@quality_analyses = QualityAnalysis.where(:project_id => @project.id).select{|qa| arr.include?(qa.sonar_name)}
2016-06-28 14:48:09 +08:00
else
2016-07-08 16:19:33 +08:00
filter = "sqale_rating,function_complexity,duplicated_lines_density,comment_lines_density,sqale_index,lines,files,functions,classes,directories,blocker_violations,critical_violations,major_violations,minor_violations,info_violations,violations"
2016-07-11 10:57:46 +08:00
complexity_date = open(@sonar_address + "/api/resources/index?resource=#{@resource_id}&depth=0&metrics=#{filter}").read
2016-06-28 14:48:09 +08:00
@complexity =JSON.parse(complexity_date).first
2016-07-08 16:19:33 +08:00
2016-08-26 15:13:07 +08:00
# 获取排名结果
@g = Gitlab.client
2016-08-30 10:43:47 +08:00
@author_infos = @g.rep_user_stats(@project.gpid, :rev => @branch)
2016-08-26 15:13:07 +08:00
@user_quality_infos = []
2016-08-30 10:43:47 +08:00
@author_infos.each do |author_info|
2016-08-26 15:13:07 +08:00
email = author_info.email
changes = author_info.changes.to_i
2016-08-30 10:43:47 +08:00
unresolved_issues = open(@sonar_address + "/api/issues/search?projectKeys=#{@resource_id}&authors=#{email}&resolved=false").read
unresolved_issue_count = JSON.parse(unresolved_issues)["total"].to_i
all_issues = open(@sonar_address + "/api/issues/search?projectKeys=#{@resource_id}&authors=#{email}").read
all_issue_count = JSON.parse(all_issues)["total"].to_i
2016-09-09 09:00:21 +08:00
ratio = ((changes == 0 || all_issue_count == 0) ? 0 : format("%0.4f",all_issue_count.to_f/changes.to_f))
2016-08-30 10:43:47 +08:00
@user_quality_infos << {:email => email, :changes => changes, :unresolved_issue_count => unresolved_issue_count, :ratio => ratio, :all_issue_count => all_issue_count}
2016-08-26 15:13:07 +08:00
end
2016-07-08 16:19:33 +08:00
# 按名称转换成hash键值对
@ha = {}
@complexity["msr"].each do |com|
key = com["key"]
if key == "sqale_index"
value = com["frmt_val"]
else
2016-07-12 16:23:30 +08:00
value = com["val"]
2016-07-08 16:19:33 +08:00
end
@ha.store(key,value)
end
2016-06-28 14:48:09 +08:00
end
rescue => e
puts e
2016-06-27 18:13:46 +08:00
end
2016-06-17 11:29:49 +08:00
end
# Find project of id params[:project_id]
def find_project_by_project_id
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
render_404
end
2016-06-21 16:46:34 +08:00
2016-07-01 09:31:05 +08:00
def find_quality_analysis
begin
@quality_analysis = QualityAnalysis.find(params[:id])
rescue
render_404
end
end
2016-06-21 16:46:34 +08:00
# Authorize the user for the requested action
def authorize(ctrl = params[:controller], action = params[:action], global = false)
unless @project.archived? && @project.gpid.nil?
true
else
render_403 :message => :notice_not_authorized_archived_project
end
end
2016-06-27 18:13:46 +08:00
def connect_jenkins
@gitlab_address = Redmine::Configuration['gitlab_address']
@jenkins_address = Redmine::Configuration['jenkins_address']
2016-07-12 16:23:30 +08:00
jenkins_username = Redmine::Configuration['jenkins_username']
jenkins_password = Redmine::Configuration['jenkins_password']
2016-06-27 18:13:46 +08:00
# connect jenkins
2016-08-10 10:58:47 +08:00
@client_jenkins = JenkinsApi::Client.new(:server_url => @jenkins_address, :username => jenkins_username, :password => jenkins_password)
2016-06-28 09:45:45 +08:00
rescue => e
logger.error("failed to connect Jenkins ==> #{e}")
2016-06-27 18:13:46 +08:00
end
2016-06-17 11:29:49 +08:00
end