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

#check_and_fix_result_time(result) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 100

def check_and_fix_result_time(result)
  if Time.now - result.created_at >= SimpleCov.merge_timeout
    puts "result #{i} has a created_at from #{result.created_at} (current time #{Time.now})"
    puts "This will prevent coverage from being combined. Please check your tests for Stub-Time-related issues"
    put "Setting result created_at to current time to avoid this issue"
    # If the result is timestamped old, it is ignored by SimpleCov
    # So we always set the created_at to Time.now so that the ResultMerger
    # doesn't discard any results
    result.created_at = Time.now
  end
end

#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.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 58

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



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 80

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)
      check_and_fix_result_time(result)
      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



112
113
114
115
116
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 112

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

#runObject

Returns false if unsuccessful Returns true if successful



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 32

def run
  # Only submit coverage to codeclimate on target branch
  if current_branch != target_branch
    puts "Current branch #{current_branch} is not the target branch #{target_branch}"
    puts "No Coverage will be reported"
    return false
  end

  # Only run on node0
  return false 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)
  true
end

#store_code_climate_payload(merged_result) ⇒ Object

Internal: Debug function, in use to log the exact file which is sent to codeclimate for use when troubleshooting.



126
127
128
129
130
131
132
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 126

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



118
119
120
121
122
# File 'lib/codeclimate_circle_ci_coverage/coverage_reporter.rb', line 118

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