Class: CoverageReporter

Inherits:
Object
  • Object
show all
Defined in:
lib/codeclimate_circle_ci_coverage/coverage_reporter.rb

Overview

This packages up the code coverage metrics from multiple servers and sends them to CodeClimate via the CodeClimate Test Reporter Gem

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target_branch) ⇒ CoverageReporter

Returns a new instance of CoverageReporter.



18
19
20
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 18

def initialize(target_branch)
  @target_branch = target_branch
end

Instance Attribute Details

#target_branchObject (readonly)

Returns the value of attribute target_branch.



16
17
18
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 16

def target_branch
  @target_branch
end

Instance Method Details

#current_branchObject



22
23
24
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 22

def current_branch
  ENV['CIRCLE_BRANCH']
end

#current_nodeObject



26
27
28
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 26

def current_node
  ENV['CIRCLE_NODE_INDEX'].to_i
end

#download_files(target_directory) ⇒ Object

Public: Download the .resultset.json files from each of the nodes and store them in the ‘target_directory`

They will be numbered 0.resultset.json, 1.resultset.json, etc.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 50

def download_files(target_directory)
  node_total = ENV['CIRCLE_NODE_TOTAL'].to_i

  # Create directory if it doesn't exist
  FileUtils.mkdir_p target_directory

  if node_total > 0
    # Copy coverage results from all nodes to circle artifacts directory
    0.upto(node_total - 1) do |i|
      node = "node#{i}"
      # Modified because circleCI doesn't appear to deal with artifacts in the expected manner
      node_project_dir = `ssh #{node} 'printf $CIRCLE_PROJECT_REPONAME'`
      from = File.join("~/", node_project_dir, 'coverage', ".resultset.json")
      to = File.join(target_directory, "#{i}.resultset.json")
      command = "scp #{node}:#{from} #{to}"

      puts "running command: #{command}"
      `#{command}`
    end
  end
end

#load_and_merge_files(source_directory, target_directory) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 72

def load_and_merge_files(source_directory, target_directory)
  FileUtils.mkdir_p target_directory
  SimpleCov.coverage_dir(target_directory)

  # Merge coverage results from all nodes
  files = Dir.glob(File.join(source_directory, "*.resultset.json"))
  files.each_with_index do |file, i|
    resultset = JSON.load(File.read(file))
    resultset.each do |_command_name, data|
      result = SimpleCov::Result.from_hash(['command', i].join => data)

      puts "Resetting result #{i} created_at from #{result.created_at} to #{Time.now}"
      # It appears that sometimes the nodes provided by CircleCI have
      # clocks which are not accurate/synchronized
      # So we always set the created_at to Time.now so that the ResultMerger
      # doesn't discard any results
      result.created_at = Time.now

      SimpleCov::ResultMerger.store_result(result)
    end
  end

  merged_result = SimpleCov::ResultMerger.merged_result
  merged_result.command_name = 'RSpec'
  merged_result
end

#output_result_html(merged_result) ⇒ Object



99
100
101
102
103
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 99

def output_result_html(merged_result)
  # Format merged result with html
  html_formatter = SimpleCov::Formatter::HTMLFormatter.new
  html_formatter.format(merged_result)
end

#runObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 30

def run
  # Only submit coverage to codeclimate on target branch
  return if current_branch != target_branch
  # Only run on node0
  return unless current_node.zero?

  all_coverage_dir = File.join("all_coverage")
  download_files(all_coverage_dir)
  final_coverage_dir = File.join("combined_coverage")

  merged_result = load_and_merge_files(all_coverage_dir, final_coverage_dir)
  output_result_html(merged_result)
  upload_result_file(merged_result)
  store_code_climate_payload(merged_result)
end

#store_code_climate_payload(merged_result) ⇒ Object

Internal: Debug function, in use to try to determine why codeclimate is marking some lines of comments as “relevant” lines.



113
114
115
116
117
118
119
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 113

def store_code_climate_payload(merged_result)
  ENV["CODECLIMATE_TO_FILE"] = "true"
  codeclimate_formatter = CodeClimate::TestReporter::Formatter.new
  codeclimate_formatter.format(merged_result)
ensure
  ENV["CODECLIMATE_TO_FILE"] = nil
end

#upload_result_file(merged_result) ⇒ Object



105
106
107
108
109
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 105

def upload_result_file(merged_result)
  # Post merged coverage result to codeclimate
  codeclimate_formatter = CodeClimate::TestReporter::Formatter.new
  codeclimate_formatter.format(merged_result)
end