class XCJobs::Coverage::Coveralls

Attributes

parallel[RW]
repo_token[RW]
service_event_type[RW]
service_job_id[RW]
service_job_number[RW]
service_name[RW]
service_number[RW]
service_pull_request[RW]

Public Class Methods

new() { |self| ... } click to toggle source
# File lib/xcjobs/coverage.rb, line 23
def initialize()
  if ENV['TRAVIS']
    @service_name = 'travis-ci'
    @service_job_id = ENV['TRAVIS_JOB_ID']
  elsif ENV['CIRCLECI']
    @service_name = 'circleci'
    @service_number = ENV['CIRCLE_BUILD_NUM']
    @service_pull_request = (ENV['CI_PULL_REQUEST'] || "")[/(\d+)$/, 1]
    @parallel = ENV['CIRCLE_NODE_TOTAL'].to_i > 1
    @service_job_number = ENV['CIRCLE_NODE_INDEX']
  end

  @extensions = []
  @excludes = []
  @exclude_patterns = []

  yield self if block_given?
  define
end

Public Instance Methods

add_exclude(exclude) click to toggle source
# File lib/xcjobs/coverage.rb, line 47
def add_exclude(exclude)
  @excludes << exclude
end
add_exclude_pattern(exclude_pattern) click to toggle source
# File lib/xcjobs/coverage.rb, line 51
def add_exclude_pattern(exclude_pattern)
  if !exclude_pattern.kind_of?(Regexp)
    exclude_pattern = Regexp.new(exclude_pattern)
  end
  @exclude_patterns << exclude_pattern
end
add_extension(extension) click to toggle source
# File lib/xcjobs/coverage.rb, line 43
def add_extension(extension)
  @extensions << extension
end

Private Instance Methods

collect(base_dir) click to toggle source
# File lib/xcjobs/coverage.rb, line 72
def collect(base_dir)
  report = {}
  report['repo_token'] = repo_token if repo_token
  report['service_name'] = service_name if service_name
  report['service_job_id'] = service_job_id if service_job_id
  report['service_number'] = service_number if service_number
  report['service_pull_request'] = service_pull_request if service_pull_request
  report['parallel'] = parallel if parallel
  report['service_job_number'] = service_job_number if service_job_number
  report['service_event_type'] = service_event_type if service_event_type
  report['source_files'] = []

  Dir.glob("#{base_dir}/**/*.gcov").each do |file|
    File.open(file, "r") do |handle|
      source_file = {}
      name = ''
      source_digest = nil
      coverage = []

      handle.each_line do |line|
        match = /^[ ]*([0-9]+|-|#####):[ ]*([0-9]+):(.*)/.match(line)
        next unless match.to_a.count == 4
        count, number, text = match.to_a[1..3]

        if number.to_i == 0
          key, val = /([^:]+):(.*)$/.match(text).to_a[1..2]
          if key == 'Source'
            name = Pathname(val).relative_path_from(Pathname(base_dir)).to_s
            if File.exist?(val)
              source_digest = Digest::MD5.file(val).to_s
            end
          end
        else
          coverage[number.to_i - 1] = case count.strip
            when '-'
              nil
            when '#####'
              if text.strip == '}'
                nil
              else
                0
              end
            else count.to_i
            end
        end
      end

      if !is_excluded_path(name) && !source_digest.nil?
        source_file['name'] = name
        source_file['source_digest'] = source_digest
        source_file['coverage'] = coverage

        report['source_files'] << source_file
      end
    end
  end

  remotes = %x[git remote -v].rstrip.split(/\r?\n/).map {|line| line.chomp }.select { |line| line.include? 'fetch'}.first.split(' ')
  report['git'] = {
    'head' => {
      'id' => %x[git --no-pager log -1 --pretty=format:%H],
      'author_name' => %x[git --no-pager log -1 --pretty=format:%aN],
      'author_email' => %x[git --no-pager log -1 --pretty=format:%ae],
      'committer_name' => %x[git --no-pager log -1 --pretty=format:%cN],
      'committer_email' => %x[git --no-pager log -1 --pretty=format:%ce],
      'message' => %x[git --no-pager log -1 --pretty=format:%s] },
    'branch' => %x[git rev-parse --abbrev-ref HEAD].strip,
    'remotes' => {'name' => remotes[0], 'remote' => remotes[1]} }

  report
end
define() click to toggle source
# File lib/xcjobs/coverage.rb, line 60
def define
  namespace :coverage do
    desc 'send coverage report to Coveralls'
    task :coveralls do
      root = %x[git rev-parse --show-toplevel].strip
      report = collect(root)
      file = write_report(report)
      upload(file)
    end
  end
end
is_excluded_path(filepath) click to toggle source
# File lib/xcjobs/coverage.rb, line 144
def is_excluded_path(filepath)
  if filepath.start_with?('..')
    return true
  end
  @excludes.each do |exclude|
    if filepath.start_with?(exclude)
      return true
    end
  end
  @exclude_patterns.each do |pattern|
    if filepath.match(pattern)
      return true
    end
  end
  if !@extensions.empty?
    @extensions.each do |extension|
      if File.extname(filepath) == extension
        return false
      end
    end
    return true
  else
    return false
  end
end
upload(json_file) click to toggle source
# File lib/xcjobs/coverage.rb, line 179
def upload(json_file)
  curl_options = ['curl', '-sSf', '-F', "json_file=@#{json_file}", 'https://coveralls.io/api/v1/jobs']
  puts curl_options.join(' ')
  Open3.popen2e({ "NSUnbufferedIO" => "YES" }, *curl_options) do |stdin, stdout_err, wait_thr|
    output = ''
    while line = stdout_err.gets
      puts line
      output << line
    end

    status = wait_thr.value
    if !status.success?
      fail "upload failed (exited with status: #{status.exitstatus})"
    end
  end
end
write_report(report) click to toggle source
# File lib/xcjobs/coverage.rb, line 170
def write_report(report)
  tempdir = Pathname.new(Dir.tmpdir).join(SecureRandom.hex)
  FileUtils.mkdir_p(tempdir)
  tempfile = File::open(tempdir.join('coveralls.json'), "w")
  tempfile.puts(report.to_json)
  tempfile.flush
  tempfile.path
end