#!/usr/bin/env ruby require 'fileutils' require 'open3' def message(str) $stderr.puts('Message: ' + str) end def warn(str) $stderr.puts('Warning: ' + str) end def fail(str) $stderr.puts('Error: ' + str) exit end $stdout.sync = true repo_clean = `git status --porcelain`.empty? unless repo_clean fail 'git repo needs to be clean to run oss-check. Aborting.' end @branch_name = `git symbolic-ref HEAD --short`.strip if @branch_name == 'master' fail "can't run osscheck from 'master' as the script compares "\ "the performance of this branch against 'master'" end @repos = [ 'Alamofire/Alamofire', 'apple/swift', 'JohnCoates/Aerial', 'jpsim/SourceKitten', 'krzysztofzablocki/Sourcery', 'kickstarter/ios-oss', 'Moya/Moya', 'mozilla-mobile/firefox-ios', 'Quick/Nimble', 'Quick/Quick', 'realm/realm-cocoa', 'wordpress-mobile/WordPress-iOS' ] @commits = {} @branch_durations = {} @master_durations = {} def convert_to_link(string) string.sub!("#{Dir.pwd}/osscheck/#{@repo_name}", '') string.sub!('.swift:', '.swift#L') string = string.partition(': warning:').first.partition(': error:').first "https://github.com/#{@repo}/blob/#{@commits[@repo]}#{string}" end def non_empty_lines(path) File.read(path).split(/\n+/).reject(&:empty?) end def generate_reports(clone, branch) Dir.chdir('osscheck') do @repos.each do |repo| repo_name = repo.partition('/').last if clone puts "Cloning #{repo_name}" `git clone "https://github.com/#{repo}" --depth 1 2> /dev/null` if repo_name == 'swift' File.open("swift/.swiftlint.yml", 'w') do |file| file << 'included: stdlib' end end end Dir.chdir(repo_name) do iterations = 5 print "Linting #{iterations} iterations of #{repo_name} with #{branch}: 1" @commits[repo] = `git rev-parse HEAD`.strip durations = [] start = Time.now command = '../../.build/release/swiftlint lint --no-cache --enable-all-rules --reporter xcode' File.open("../#{branch}_reports/#{repo_name}.txt", 'w') do |file| Open3.popen3(command) do |_, stdout, _, _| file << stdout.read.chomp end end durations += [Time.now - start] for i in 2..iterations print "..#{i}" start = Time.now Open3.popen3(command) { |_, stdout, _, _| stdout.read } durations += [Time.now - start] end puts '' average_duration = (durations.reduce(:+) / iterations).round(2) if branch == 'branch' @branch_durations[repo] = average_duration else @master_durations[repo] = average_duration end end end end end # Prep ['osscheck/branch_reports', 'osscheck/master_reports'].each do |dir| FileUtils.mkdir_p(dir) end # Build branch puts 'Building branch' `swift build -c release` # Generate branch reports generate_reports(true, 'branch') # Build master `git fetch` `git checkout origin/master` puts 'Building master' `swift build -c release` unless $?.success? # Couldn't build, start fresh FileUtils.rm_rf %w(Packages .build) return_value = nil Open3.popen3('swift build -c release') do |_, stdout, _, wait_thr| puts stdout.read.chomp return_value = wait_thr.value end unless return_value.success? fail 'Could not build master' return end end # Generate master reports generate_reports(false, 'master') # Diff and report changes to Danger @repos.each do |repo| @repo_name = repo.partition('/').last branch = non_empty_lines("osscheck/branch_reports/#{@repo_name}.txt") master = non_empty_lines("osscheck/master_reports/#{@repo_name}.txt") @repo = repo (master - branch).each do |fixed| message "This PR fixed a violation in #{@repo_name}: [#{fixed}](#{convert_to_link(fixed)})" end (branch - master).each do |violation| warn "This PR introduced a violation in #{@repo_name}: [#{violation}](#{convert_to_link(violation)})" end end @repos.each do |repo| branch_duration = @branch_durations[repo] master_duration = @master_durations[repo] percent_change = 100 * (master_duration - branch_duration) / master_duration faster_slower = nil if branch_duration < master_duration faster_slower = 'faster' else faster_slower = 'slower' percent_change *= -1 end repo_name = repo.partition('/').last message "Linting #{repo_name} with this PR took #{branch_duration}s " \ "vs #{master_duration}s on master (#{percent_change.to_i}\% #{faster_slower})" end # Clean up `git reset --hard HEAD` FileUtils.rm_rf('osscheck') `git checkout #{@branch_name}`