socialforge/app/controllers/quality_analysis_controller.rb

319 lines
13 KiB
Ruby
Raw 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.

class QualityAnalysisController < ApplicationController
before_filter :find_project_by_project_id#, :except => [:getattachtype]
before_filter :find_quality_analysis, :only => [:edit, :update_jenkins_job]
before_filter :authorize
before_filter :connect_jenkins, :only => [:create, :edit, :update_jenkins_job, :index, :delete]
layout "base_projects"
include ApplicationHelper
include QualityAnalysisHelper
# require 'jenkins_api_client'
require 'nokogiri'
require 'json'
require 'open-uri'
require 'uri'
def show
end
# params 说明:{identifier版本库名}
# type: 1 新的分析 2 重新分析
def create
begin
user_name = User.find(params[:user_id]).try(:login)
identifier = params[:identifier]
rep_id = params[:rep_id]
# job_name and sonar_name 前者为job名字后者为jenkins配置名
job_name = "#{user_name}-#{rep_id}"
sonar_name = "#{user_name}:#{rep_id}"
# 考虑到历史数据有些用户创建类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?
# 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}
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"
# 替换配置文件
@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
break
end
end
end
# sonar 缓冲sonar生成数据
sleep(10)
# 获取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}")
# 两种情况需要删除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)
@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}")
end
end
end
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)}
end
end
rescue => e
@message = e.message
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}
end
end
end
def error_list
# 顶部导航
@project_menu_type = 5
@error_list = SonarError.where(:jenkins_job_name => params[:job_name]).first
respond_to do |format|
format.html
end
end
# get language type
def swith_language_type language
if language == "c#"
"cs"
elsif language == "python"
"py"
elsif language == "c"
"c++"
else
language
end
end
def edit
@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
end
# 删除的时候主要删除三方面数据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}")
logger.info("result: @client_jenkins.job ###################==>#{@client_jenkins.job}")
d_job = @client_jenkins.job.delete(job_name)
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}
end
rescue Exception => e
puts e
end
end
# 更新Jenkins job主要包括相关配置文件参数的更新Trustie平台数据的更新
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
# update成功则返回 200
jenkins_job = @client_jenkins.job.update("#{job_name}", @doc.to_xml)
# 数据更新到Trustie数据
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
end
# resource_id: login + @repository.id
def index
# 顶部导
@project_menu_type = 5
begin
@branch = params[:branch]
@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"]
@quality_analyses = QualityAnalysis.where(:project_id => @project.id).select{|qa| arr.include?(qa.sonar_name)}
else
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"
complexity_date = open(@sonar_address + "/api/resources/index?resource=#{@resource_id}&depth=0&metrics=#{filter}").read
@complexity =JSON.parse(complexity_date).first
logger.info("######################################complexity is #{@complexity}")
# 获取排名结果
@g = Gitlab.client
@author_infos = @g.rep_user_stats(@project.gpid, :rev => @branch)
@user_quality_infos = []
@author_infos.each do |author_info|
email = author_info.email
changes = author_info.changes.to_i
unresolved_issues = open(URI::escape(@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(URI::escape(@sonar_address + "/api/issues/search?projectKeys=#{@resource_id}&authors=#{email}")).read
all_issue_count = JSON.parse(all_issues)["total"].to_i
ratio = ((changes == 0 || all_issue_count == 0) ? 0 : format("%0.4f",all_issue_count.to_f/changes.to_f))
@user_quality_infos << {:email => email, :changes => changes, :unresolved_issue_count => unresolved_issue_count, :ratio => ratio, :all_issue_count => all_issue_count}
end
# 按名称转换成hash键值对
@ha = {}
@complexity["msr"].each do |com|
key = com["key"]
if key == "sqale_index"
value = com["frmt_val"]
else
value = com["val"]
end
@ha.store(key,value)
end
end
rescue Exception => e
logger.error("fatal################# #{e}")
end
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
def find_quality_analysis
begin
@quality_analysis = QualityAnalysis.find(params[:id])
rescue
render_404
end
end
# 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
def connect_jenkins
@gitlab_address = Redmine::Configuration['gitlab_address']
@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)
rescue => e
logger.error("failed to connect Jenkins ==> #{e}")
end
end