You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

quality_analysis_controller.rb 14 kB

10 years ago
10 years ago
10 years ago
10 years ago
​
10 years ago
​
​
​
10 years ago
​
​
​
10 years ago
10 years ago
10 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
10 years ago
10 years ago
​
​
​
​
​
​
10 years ago
10 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
10 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
10 years ago
​
​
​
10 years ago
10 years ago
​
​
​
​
10 years ago
​
10 years ago
10 years ago
10 years ago
​
​
​
10 years ago
10 years ago
10 years ago
​
​
​
​
​
​
​
10 years ago
​
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
​
​
​
​
​
10 years ago
​
​
​
10 years ago
​
10 years ago
​
​
​
​
​
​
​
10 years ago
10 years ago
10 years ago
10 years ago
​
​
10 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
10 years ago
10 years ago
​
​
​
​
​
​
​
​
​
​
​
​
​
10 years ago
​
10 years ago
​
​
10 years ago
​
​
​
​
10 years ago
​
10 years ago
10 years ago
​
​
​
​
10 years ago
​
​
​
​
​
​
​
10 years ago
​
​
​
​
10 years ago
​
​
​
​
​
​
​
10 years ago
​
​
​
​
​
​
​
10 years ago
​
​
​
​
​
​
​
​
​
​
10 years ago
​
10 years ago
10 years ago
​
​
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. class QualityAnalysisController < ApplicationController
  2. before_filter :find_project_by_project_id#, :except => [:getattachtype]
  3. before_filter :find_quality_analysis, :only => [:edit, :update_jenkins_job]
  4. before_filter :authorize
  5. before_filter :connect_jenkins, :only => [:create, :edit, :update_jenkins_job, :index, :delete]
  6. layout "base_projects"
  7. include ApplicationHelper
  8. include QualityAnalysisHelper
  9. require 'jenkins_api_client'
  10. require 'nokogiri'
  11. require 'json'
  12. require 'open-uri'
  13. def show
  14. end
  15. # params 说明:{identifier:版本库名}
  16. # type: 1 新的分析 2 重新分析
  17. def create
  18. begin
  19. user_name = User.find(params[:user_id]).try(:login)
  20. identifier = params[:identifier]
  21. rep_id = params[:rep_id]
  22. # job_name and sonar_name 前者为job名字,后者为jenkins配置名
  23. job_name = "#{user_name}-#{rep_id}"
  24. sonar_name = "#{user_name}:#{rep_id}"
  25. # 考虑到历史数据:有些用户创建类job但是build失败,即sonar没有结果,这个时候需要把job删除,并且删掉quality_analyses表数据
  26. # 如果不要这句则需要迁移数据
  27. @sonar_address = Redmine::Configuration['sonar_address']
  28. # projects_date = open(@sonar_address + "/api/projects/index").read
  29. # arr = JSON.parse(projects_date).map {|m| m["nm"]} # eg: ["Hjqreturn:cc_rep", "Hjqreturn:putong", "Hjqreturn:sonar_rep2", "shitou:sonar_rep"]
  30. quality_an = QualityAnalysis.where(:sonar_name => sonar_name)
  31. # if @client_jenkins.job.exists?(job_name) && QualityAnalysis.where(:sonar_name => sonar_name).select{|qa| arr.include?(qa.sonar_name)}.blank?
  32. # aa = @client_jenkins.job.delete("#{job_name}")
  33. # quality_an.delete unless quality_an.blank?
  34. # end
  35. # type 1的时候之所以判断job是否存在,为了防止特殊情况,正常情况是不会出现的
  36. # 重新分析的时候需要删除以前的分析结果
  37. @client_jenkins.job.delete("#{job_name}") if @client_jenkins.job.exists?(job_name)
  38. quality_an.delete_all unless quality_an.blank?
  39. # Checks if the given job exists in Jenkins.
  40. @g = Gitlab.client
  41. branch = params[:branch]
  42. language = swith_language_type(params[:language])
  43. path = params[:path].blank? ? "./" : params[:path]
  44. # qa = QualityAnalysis.where(:project_id => @project.id, :author_login => user_name).first
  45. version = 1
  46. properties = "sonar.projectKey=#{sonar_name}
  47. sonar.projectName=#{sonar_name}
  48. sonar.projectVersion=#{version}
  49. sonar.sources=#{path}
  50. sonar.language=#{language.downcase}
  51. sonar.sourceEncoding=utf-8"
  52. git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git"
  53. # 替换配置文件
  54. @doc = Nokogiri::XML(File.open(File.join(Rails.root, 'tmp', 'config.xml')))
  55. @doc.at_xpath("//hudson.plugins.git.UserRemoteConfig/url").content = git_url
  56. @doc.at_xpath("//hudson.plugins.git.BranchSpec/name").content = "*/#{branch}"
  57. @doc.at_xpath("//hudson.plugins.sonar.SonarRunnerBuilder/properties").content = properties # sonar-properties
  58. # jenkins job创建
  59. jenkins_job = @client_jenkins.job.create("#{job_name}", @doc.to_xml)
  60. # 将地址作为hook值添加到gitlab
  61. # @g.add_project_hook(@project.gpid, @jenkins_address + "/project/#{job_name}")
  62. # job创建完成后自动运行job,如果运行成功则返回‘200’
  63. code = @client_jenkins.job.build("#{job_name}")
  64. # 判断调用sonar分析是否成功
  65. # 等待启动时间处理, 最长时间为30分钟
  66. for i in 0..360 do
  67. sleep(5)
  68. @current_build_status = @client_jenkins.job.get_current_build_status("#{job_name}")
  69. if (@current_build_status == "success" || @current_build_status == "failure")
  70. break
  71. if i == 360
  72. @build_console_result = false
  73. break
  74. end
  75. end
  76. end
  77. # sonar 缓冲,sonar生成数据
  78. sleep(10)
  79. # 获取sonar output结果
  80. console_build = @client_jenkins.job.get_console_output("#{job_name}", build_num = 0, start = 0, mode = 'text')["output"]
  81. logger.info("@current_build_status is ==> #{@current_build_status}")
  82. # 两种情况需要删除job:
  83. # 1/创建成功但是build失败则删除job
  84. # 2/creat和build成功,调用sonar启动失败则删除job
  85. # 错误信息存储需存到Trustie数据库,否则一旦job删除则无法获取这些信息
  86. if jenkins_job == '200' && code != '201'
  87. @client_jenkins.job.delete("#{job_name}")
  88. else
  89. if @current_build_status == "failure"
  90. reg_console = /Exception:.*?\r/.match(console_build)
  91. output = reg_console[0].gsub("\r", "") unless reg_console.nil?
  92. se = SonarError.where(:jenkins_job_name => job_name).first
  93. se.nil? ? SonarError.create(:project_id => @project.id, :jenkins_job_name => job_name, :output => output) : se.update_column(:output, output)
  94. @client_jenkins.job.delete("#{job_name}")
  95. elsif @current_build_status == "success"
  96. if quality_an.blank?
  97. QualityAnalysis.create(:project_id => @project.id, :author_login => user_name, :rep_identifier => identifier,
  98. :sonar_version => version, :path => path, :branch => branch, :language => language, :sonar_name => "#{user_name}:#{rep_id}")
  99. end
  100. end
  101. end
  102. respond_to do |format|
  103. if @current_build_status == "success"
  104. 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)}
  105. elsif @current_build_status == "failure"
  106. format.html{redirect_to error_list_project_quality_analysi_path(:project_id => @project.id, :job_name => job_name)}
  107. end
  108. end
  109. rescue => e
  110. @message = e.message
  111. logger.error("######################====>#{e.message}")
  112. respond_to do |format|
  113. format.html{redirect_to error_list_project_quality_analysi_path(:project_id => @project.id, :job_name => job_name, :message => @message)}
  114. # format.html{redirect_to :controller => 'repositories', :action => 'show', :id => @project, :repository_id => identifier}
  115. end
  116. end
  117. end
  118. def error_list
  119. # 顶部导航
  120. @project_menu_type = 5
  121. @error_list = SonarError.where(:jenkins_job_name => params[:job_name]).first
  122. respond_to do |format|
  123. format.html
  124. end
  125. end
  126. # get language type
  127. def swith_language_type language
  128. if language == "c#"
  129. "cs"
  130. elsif language == "python"
  131. "py"
  132. elsif language == "c"
  133. "c++"
  134. else
  135. language
  136. end
  137. end
  138. def edit
  139. @g = Gitlab.client
  140. gitlab_branches = @g.branches(@project.gpid)
  141. @branch_names = gitlab_branches.map{|b| b.name}
  142. @gitlab_default_branch = @g.project(@project.gpid).default_branch
  143. end
  144. # 删除的时候主要删除三方面数据:1/Trustie数据 2/jenkins数据 3/sonar数据
  145. # 如果只删除数据1,则新建的时候会有冲突
  146. def delete
  147. begin
  148. qa = QualityAnalysis.find(params[:id])
  149. rep_id = Repository.where(:project_id => @project.id, :identifier => qa.rep_identifier).first.try(:id)
  150. job_name = "#{qa.author_login}-#{rep_id}"
  151. logger.info("result: job_name ###################==>#{job_name}")
  152. logger.info("result: @client_jenkins.job ###################==>#{@client_jenkins.job}")
  153. d_job = @client_jenkins.job.delete(job_name)
  154. logger.info("result: delete job ###################==>#{d_job}")
  155. qa.delete
  156. respond_to do |format|
  157. format.html{redirect_to :controller => 'repositories', :action => 'show', :id => @project, :repository_id => gitlab_repository(@project).identifier}
  158. end
  159. rescue Exception => e
  160. puts e
  161. end
  162. end
  163. # 更新Jenkins job,主要包括相关配置文件参数的更新,Trustie平台数据的更新
  164. def update_jenkins_job
  165. begin
  166. rep_id = Repository.where(:project_id => @project.id).first.try(:id)
  167. sonar_name = @quality_analysis.sonar_name
  168. job_name = "#{@quality_analysis.author_login}-#{rep_id}"
  169. version = @quality_analysis.sonar_version
  170. path = params[:path].blank? ? "./" : params[:path]
  171. language = swith_language_type(params[:language])
  172. branch = params[:branch]
  173. identifier = @quality_analysis.rep_identifier
  174. properties = "sonar.projectKey=#{sonar_name}
  175. sonar.projectName=#{sonar_name}
  176. sonar.projectVersion=#{version}
  177. sonar.sources=#{path}
  178. sonar.language=#{language.downcase}
  179. sonar.sourceEncoding=utf-8"
  180. git_url = @gitlab_address.to_s+"/"+@project.owner.to_s+"/"+ identifier + "."+"git"
  181. # modify config.yml
  182. @doc = Nokogiri::XML(File.open(File.join(Rails.root, 'tmp', 'config.xml')))
  183. @doc.at_xpath("//hudson.plugins.git.UserRemoteConfig/url").content = git_url
  184. @doc.at_xpath("//hudson.plugins.git.BranchSpec/name").content = "*/#{branch}"
  185. @doc.at_xpath("//hudson.plugins.sonar.SonarRunnerBuilder/properties").content = properties # sonar-properties
  186. # update成功则返回 ‘200’
  187. jenkins_job = @client_jenkins.job.update("#{job_name}", @doc.to_xml)
  188. # 数据更新到Trustie数据
  189. if jenkins_job == '200'
  190. logger.info("quality_ananlysis will be updated: ==> #{jenkins_job}")
  191. @quality_analysis.path = path
  192. @quality_analysis.language = language
  193. @quality_analysis.branch = branch
  194. @quality_analysis.save
  195. end
  196. rescue Exception => e
  197. logger.error("Update jenkins job: #{e}")
  198. end
  199. respond_to do |format|
  200. format.html{redirect_to project_quality_analysis_path(:project_id => @project.id)}
  201. format.js
  202. end
  203. end
  204. # resource_id: login + @repository.id
  205. def index
  206. # 顶部导航
  207. @project_menu_type = 5
  208. begin
  209. @branch = params[:branch]
  210. @resource_id = params[:resource_id]
  211. @sonar_address = Redmine::Configuration['sonar_address']
  212. if params[:resource_id].nil?
  213. @name_flag = true
  214. projects_date = open(@sonar_address + "/api/projects/index").read
  215. arr = JSON.parse(projects_date).map {|m| m["nm"]} # eg: ["Hjqreturn:cc_rep", "Hjqreturn:putong", "Hjqreturn:sonar_rep2", "shitou:sonar_rep"]
  216. @quality_analyses = QualityAnalysis.where(:project_id => @project.id).select{|qa| arr.include?(qa.sonar_name)}
  217. else
  218. 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"
  219. complexity_date = open(@sonar_address + "/api/resources/index?resource=#{@resource_id}&depth=0&metrics=#{filter}").read
  220. @complexity =JSON.parse(complexity_date).first
  221. # 获取排名结果
  222. @g = Gitlab.client
  223. @author_infos = @g.rep_user_stats(@project.gpid, :rev => @branch)
  224. @user_quality_infos = []
  225. @author_infos.each do |author_info|
  226. email = author_info.email
  227. changes = author_info.changes.to_i
  228. unresolved_issues = open(@sonar_address + "/api/issues/search?projectKeys=#{@resource_id}&authors=#{email}&resolved=false").read
  229. unresolved_issue_count = JSON.parse(unresolved_issues)["total"].to_i
  230. all_issues = open(@sonar_address + "/api/issues/search?projectKeys=#{@resource_id}&authors=#{email}").read
  231. all_issue_count = JSON.parse(all_issues)["total"].to_i
  232. ratio = ((changes == 0 || all_issue_count == 0) ? 0 : format("%0.4f",all_issue_count.to_f/changes.to_f))
  233. @user_quality_infos << {:email => email, :changes => changes, :unresolved_issue_count => unresolved_issue_count, :ratio => ratio, :all_issue_count => all_issue_count}
  234. end
  235. # 按名称转换成hash键值对
  236. @ha = {}
  237. @complexity["msr"].each do |com|
  238. key = com["key"]
  239. if key == "sqale_index"
  240. value = com["frmt_val"]
  241. else
  242. value = com["val"]
  243. end
  244. @ha.store(key,value)
  245. end
  246. end
  247. rescue => e
  248. puts e
  249. end
  250. end
  251. # Find project of id params[:project_id]
  252. def find_project_by_project_id
  253. @project = Project.find(params[:project_id])
  254. rescue ActiveRecord::RecordNotFound
  255. render_404
  256. end
  257. def find_quality_analysis
  258. begin
  259. @quality_analysis = QualityAnalysis.find(params[:id])
  260. rescue
  261. render_404
  262. end
  263. end
  264. # Authorize the user for the requested action
  265. def authorize(ctrl = params[:controller], action = params[:action], global = false)
  266. unless @project.archived? && @project.gpid.nil?
  267. true
  268. else
  269. render_403 :message => :notice_not_authorized_archived_project
  270. end
  271. end
  272. def connect_jenkins
  273. @gitlab_address = Redmine::Configuration['gitlab_address']
  274. @jenkins_address = Redmine::Configuration['jenkins_address']
  275. jenkins_username = Redmine::Configuration['jenkins_username']
  276. jenkins_password = Redmine::Configuration['jenkins_password']
  277. # connect jenkins
  278. @client_jenkins = JenkinsApi::Client.new(:server_url => @jenkins_address, :username => jenkins_username, :password => jenkins_password)
  279. rescue => e
  280. logger.error("failed to connect Jenkins ==> #{e}")
  281. end
  282. end

软件技术创新是当今国家整体创新能力的重要基础和发展引擎。近年来,大众参与的软件创新与创业活动已经成为网络时代软件开发和应用的新形态,正在快速改变着全球软件创新模式和软件产业格局。 ...